За счет применения расширяющих методов появляется возможность модифицировать типы, не создавая подклассов и не изменяя код типа напрямую. Загвоздка в том, что новая функциональность предлагается типом, только если в текущем проекте будут присутствовать ссылки на расширяющие методы.
Определение расширяющих методов
Первое ограничение, связанное с расширяющими методами, состоит в том, что они должны быть определены внутри статического класса (см. главу 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 и затем получить все его символы.
char[] digits = i.ToString().ToCharArray();
// Изменить порядок следования элементов массива.
Array.Reverse(digits);
// Поместить обратно в строку.
string newDigits = new string(digits);
// Возвратить модифицированную строку как int.
return int.Parse(newDigits);
}
}
}
Снова обратите внимание на то, что первый параметр каждого расширяющего метода снабжен ключевым словом
this
, находящимся перед определением типа параметра. Первый параметр расширяющего метода всегда представляет расширяемый тип. Учитывая, что метод
DisplayDefiningAssembly()
был прототипирован для расширения
System.Object
, этот новый член теперь присутствует в каждом типе, поскольку
Object
является родительским для всех типов платформы .NET Core. Однако метод
ReverseDigits()
прототипирован для расширения только целочисленных типов, и потому если к нему обращается какое-то другое значение, то возникнет ошибка на этапе компиляции.
На заметку! Запомните, что каждый расширяющий метод может иметь множество параметров, но только первый параметр разрешено помечать посредством
this
. Дополнительные параметры будут трактоваться как нормальные входные параметры, применяемые методом.
Вызов расширяющих методов
Располагая созданными расширяющими методами, рассмотрим следующий код, в котором они используются с разнообразными типами из библиотек базовых классов:
using System;
using MyExtensionMethods;
Console.WriteLine("***** Fun with Extension Methods *****\n");
// В int появилась новая отличительная черта!
int myInt = 12345678;
myInt.DisplayDefiningAssembly();
// И в SoundPlayer!
System.Data.DataSet d = new System.Data.DataSet();
d.DisplayDefiningAssembly();
// Использовать новую функциональность int.
Console.WriteLine("Value of myInt: {0}", myInt);
Console.WriteLine("Reversed digits of myInt: {0}",
myInt.ReverseDigits());
Console.ReadLine();
Ниже показан вывод:
***** Fun with Extension Methods *****