Литмир - Электронная Библиотека
A
A

// Что?! Понять это непросто...

MiniVan newVan = myVan * yourVan;

Перегрузка операций обычно полезна только при построении атомарных типов данных. Векторы, матрицы, текст, точки, фигуры, множества и т.п. будут подходящими кандидатами на перегрузку операций, но люди, менеджеры, автомобили, подключения к базе данных и веб-страницы — нет. В качестве эмпирического правила запомните, что если перегруженная операция затрудняет понимание пользователем функциональности типа, то не перегружайте ее. Используйте такую возможность с умом.

Понятие специальных преобразований типов

Давайте теперь обратимся к теме, тесно связанной с перегрузкой операций, а именно — к специальным преобразованиям типов. Чтобы заложить фундамент для последующего обсуждения, кратко вспомним понятие явных и неявных преобразований между числовыми данными и связанными типами классов.

Повторение: числовые преобразования

В терминах встроенных числовых типов (

sbyte
,
int
,
float
и т.д.) явное преобразование требуется, когда вы пытаетесь сохранить большее значение в контейнере меньшего размера, т.к. подобное действие может привести к утере данных. По существу тем самым вы сообщаете компилятору, что отдаете себе отчет в том, что делаете. И наоборот — неявное преобразование происходит автоматически, когда вы пытаетесь поместить меньший тип в больший целевой тип, что не должно вызвать потерю данных:

int a = 123;

long b = a;       // Неявное преобразование из int в long.

int c = (int) b;  // Явное преобразование из long в int.

Повторение: преобразования между связанными типами классов

В главе 6 было показано, что типы классов могут быть связаны классическим наследованием (отношение "является"). В таком случае процесс преобразования C# позволяет осуществлять приведение вверх и вниз по иерархии классов. Например, производный класс всегда может быть неявно приведен к базовому классу. Тем не менее, если вы хотите сохранить объект базового класса в переменной производного класса, то должны выполнить явное приведение:

// Два связанных типа классов.

class Base{}

class Derived : Base{}

// Неявное приведение производного класса к базовому.

Base myBaseType;

myBaseType = new Derived();

// Для сохранения ссылки на базовый класс в переменной

// производного класса требуется явное преобразование.

Derived myDerivedType = (Derived)myBaseType;

Продемонстрированное явное приведение работает из-за того, что классы

Base
и
Derived
связаны классическим наследованием, а объект
myBaseType
создан как экземпляр
Derived
. Однако если
myBaseType
является экземпляром
Base
, тогда приведение вызывает генерацию исключения
InvalidCastException
. При наличии сомнений по поводу успешности приведения вы должны использовать ключевое слово
as
, как обсуждалось в главе 6. Ниже показан переделанный пример:

// Неявное приведение производного класса к базовому.

Base myBaseType2 = new();

// Сгенерируется исключение InvalidCastException :

// Derived myDerivedType2 = (Derived)myBaseType2 as Derived;

// Исключения нет, myDerivedType2 равен null:

Derived myDerivedType2 = myBaseType2 as Derived;

Но что если есть два типа классов в разных иерархиях без общего предка (кроме

System.Object
), которые требуют преобразований? Учитывая, что они не связаны классическим наследованием, типичные операции приведения здесь не помогут (и вдобавок компилятор сообщит об ошибке).

В качестве связанного замечания обратимся к типам значений (структурам). Предположим, что имеются две структуры с именами

Square
и
Rectangle
. Поскольку они не могут задействовать классическое наследование (т.к. запечатаны), не существует естественного способа выполнить приведение между этими по внешнему виду связанными типами.

Несмотря на то что в структурах можно было бы создать вспомогательные методы (наподобие

Rectangle.ToSquare()
), язык C# позволяет строить специальные процедуры преобразования, которые дают типам возможность реагировать на операцию приведения
()
. Следовательно, если корректно сконфигурировать структуры, тогда для явного преобразования между ними можно будет применять такой синтаксис:

// Преобразовать Rectangle в Square!

Rectangle rect = new Rectangle

{

  Width = 3;

  Height = 10;

}

Square sq = (Square)rect;

Создание специальных процедур преобразования

Начните с создания нового проекта консольного приложения по имени

CustomConversions
. В языке C# предусмотрены два ключевых слова,
explicit
и
implicit
, которые можно использовать для управления тем, как типы должны реагировать на попытку преобразования. Предположим, что есть следующие определения структур:

using System;

namespace CustomConversions

{

  public struct Rectangle

  {

    public int Width {get; set;}

    public int Height {get; set;}

    public Rectangle(int w, int h)

    {

      Width = w;

      Height = h;

    }

221
{"b":"847442","o":1}