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

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

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

Первое ограничение, связанное с расширяющими методами, состоит в том, что они должны быть определены внутри статического класса (см. главу 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 *****

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