Шрифт:
Интервал:
Закладка:
...
// Преобразование int в Square.
Square sq2 = (Square)90;
Console.WriteLine("sq2 = {0}", sq2);
// Преобразование Square в int.
int side = (int)sq2;
Console.WriteLine("Side length of sq2 = {0}", side);
Console.ReadLine();
Ниже показаны изменения, внесенные в структуру Square:
public struct Square
{
...
public static explicit operator Square(int sideLength)
{
Square newSq = new Square {Length = sideLength};
return newSq;
}
public static explicit operator int (Square s) => s.Length;
}
По правде говоря, преобразование Square в int может показаться не слишком интуитивно понятной (или полезной) операцией (скорее всего, вы просто будете передавать нужные значения конструктору). Тем не менее, оно указывает на важный факт, касающийся процедур специальных преобразований: компилятор не беспокоится о том, из чего и во что происходит преобразование, до тех пор, пока вы пишете синтаксически корректный код.
Таким образом, как и с перегрузкой операций, возможность создания операции явного приведения для заданного типа вовсе не означает необходимость ее создания. Обычно этот прием будет наиболее полезным при создании типов структур, учитывая, что они не могут принимать участие в классическом наследовании (где приведение обеспечивается автоматически).
Определение процедур неявного преобразования
До сих пор мы создавали различные специальные операции явного преобразования. Но что насчет следующего неявного преобразования?
...
Square s3 = new Square {Length = 83};
// Попытка сделать неявное приведение?
Rectangle rect2 = s3;
Console.ReadLine();
Данный код не скомпилируется, т.к. вы не предоставили процедуру неявного преобразования для типа Rectangle. Ловушка здесь вот в чем: определять одновременно функции явного и неявного преобразования не разрешено, если они не различаются по типу возвращаемого значения или по списку параметров. Это может показаться ограничением; однако вторая ловушка связана с тем, что когда тип определяет процедуру неявного преобразования, то вызывающий код вполне законно может использовать синтаксис явного приведения!
Запутались? Чтобы прояснить ситуацию, давайте добавим к структуре Rectangle процедуру неявного преобразования с применением ключевого слова implicit (обратите внимание, что в показанном ниже коде предполагается, что ширина результирующего прямоугольника вычисляется умножением стороны квадрата на 2):
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})public struct Rectangle
{
...
public static implicit operator Rectangle(Square s)
{
Rectangle r = new Rectangle
{
Height = s.Length,
Width = s.Length * 2 // Предположим, что ширина нового
// квадрата будет равна (Length х 2)..
};
return r;
}
}
После такой модификации можно выполнять преобразование между типами:
...
// Неявное преобразование работает!
Square s3 = new Square { Length= 7};
Rectangle rect2 = s3;
Console.WriteLine("rect2 = {0}", rect2);
// Синтаксис явного приведения также работает!
Square s4 = new Square {Length = 3};
Rectangle rect3 = (Rectangle)s4;
Console.WriteLine("rect3 = {0}", rect3);
Console.ReadLine();
На этом обзор определения операций специального преобразования завершен. Как и с перегруженными операциями, помните о том, что данный фрагмент синтаксиса представляет собой просто сокращенное обозначение для "нормальных" функций-членов и потому всегда необязателен. Тем не менее, в случае правильного использования специальные структуры могут применяться более естественным образом, поскольку будут трактоваться как настоящие типы классов, связанные наследованием.
Понятие расширяющих методов
В версии .NET 3.5 появилась концепция расширяющих методов, которая позволила добавлять новые методы или свойства к классу либо структуре, не модифицируя исходный тип непосредственно. Когда такой прием может оказаться полезным? Рассмотрим следующие ситуации.
Пусть есть класс, находящийся в производстве. Со временем выясняется, что имеющийся класс должен поддерживать несколько новых членов. Изменение текущего определения класса напрямую сопряжено с риском нарушения обратной совместимости со старыми кодовыми базами, использующими его, т.к. они могут не скомпилироваться с последним улучшенным определением класса. Один из способов обеспечения обратной совместимости предусматривает создание нового класса, производного от существующего, но тогда придется сопровождать два класса. Как все мы знаем, сопровождение кода является самой скучной частью деятельности разработчика программного обеспечения.
Представим другую ситуацию. Предположим, что имеется структура (или, может быть, запечатанный класс), и необходимо добавить новые члены, чтобы получить полиморфное поведение в рамках системы. Поскольку структуры и запечатанные классы не могут быть расширены, единственный выбор заключается в том, чтобы добавить желаемые члены к типу, снова рискуя нарушить обратную совместимость!
- Понимание SQL - Мартин Грубер - Базы данных