Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Запутались? Чтобы прояснить ситуацию, давайте добавим к структуре

Rectangle
процедуру неявного преобразования с применением ключевого слова
implicit
(обратите внимание, что в показанном ниже коде предполагается, что ширина результирующего прямоугольника вычисляется умножением стороны квадрата на 2):

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 появилась концепция расширяющих методов, которая позволила добавлять новые методы или свойства к классу либо структуре, не модифицируя исходный тип непосредственно. Когда такой прием может оказаться полезным? Рассмотрим следующие ситуации.

Пусть есть класс, находящийся в производстве. Со временем выясняется, что имеющийся класс должен поддерживать несколько новых членов. Изменение текущего определения класса напрямую сопряжено с риском нарушения обратной совместимости со старыми кодовыми базами, использующими его, т.к. они могут не скомпилироваться с последним улучшенным определением класса. Один из способов обеспечения обратной совместимости предусматривает создание нового класса, производного от существующего, но тогда придется сопровождать два класса. Как все мы знаем, сопровождение кода является самой скучной частью деятельности разработчика программного обеспечения.

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

За счет применения расширяющих методов

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

Определение расширяющих методов

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

static
. Вторая проблема в том, что все расширяющие методы помечаются как таковые посредством ключевого слова
this
в качестве модификатора первого (и только первого) параметра заданного метода. Параметр, помеченный с помощью
this
, представляет расширяемый элемент.

В целях иллюстрации создайте новый проект консольного приложения под названием

ExtensionMethods
. Предположим, что создается класс по имени
МуExtensions
, в котором определены два расширяющих метода. Первый расширяющий метод позволяет объекту любого типа взаимодействовать с новым методом
DisplayDefiningAssembly
, который использует типы из пространства имен
System.Reflection
для отображения имени сборки, содержащей данный тип.

На заметку! API-интерфейс рефлексии формально рассматривается в главе 17. Если эта тема для вас нова, тогда просто запомните, что рефлексия позволяет исследовать структуру сборок, типов и членов типов во время выполнения.

Второй расширяющий метод по имени

ReverseDigits
позволяет любому значению типа
int
получить новую версию самого себя с обратным порядком следования цифр. Например, если целочисленное значение
1234
вызывает
ReverseDigits
, то в результате возвратится
4321
. Взгляните на следующую реализацию класса (не забудьте импортировать пространство имен
System.Reflection
):

using System;

using System.Reflection;

namespace MyExtensionMethods

{

static class MyExtensions

{

// Этот метод позволяет объекту любого типа

// отобразить сборку, в которой он определен

public static void DisplayDefiningAssembly(this object obj)

{

Console.WriteLine("{0} lives here: => {1}\n",

obj.GetType.Name,

Assembly.GetAssembly(obj.GetType).GetName.Name);

}

// Этот метод позволяет любому целочисленному значению изменить

// порядок следования десятичных цифр на обратный.

// Например, для 56 возвратится 65.

public static int ReverseDigits(this int i)

{

// Транслировать int в string и затем получить все его символы.

Поделиться с друзьями: