Секреты LINQ. Автогенерируемые свойства. Инициализаторы объектов и коллекций, страница 13

public static class Extender    // Имя класса произвольно

{

// Главную роль в списке параметров метода играет описатель this. Второй параметр (коллекция col) играет вспомогательную роль

public static bool IsIn (this object o, IEnumerable col)

{

foreach (var v in col)

{

if (v.Equals(o))

return true;

}

return false;

}

}

Новый, необычный контекст использования ключевого слова this используется в .NET Framework 3.5 для включения механизма extension. Обратите внимание на первый параметр метода IsIn (this object o). Именно он определяет расширяемый тип. Описатель this явно указывает, что метод IsIn является расширением. Далее следует тип, который следует расширить. В нашем случае им является object.

Проверить результат расширения функциональности класса Object можно в рамках любого другого класса (например, в стандартном классе консольного проекта — Program). Важно лишь обеспечить видимость класса Extender. Для этого поместите класс Extender в то же пространство имен, в котором живет класс Program консольного проекта, и добавьте код обращения к методу IsIn. Например.

static void Main()

{

var teams = new string[] { "Spartak", "Zenith", "Locomotive" };

bool b = "Zenith".IsIn(teams);

Console.WriteLine("Zenith is in teams array: " + b);

var a = new[] { 1, 2, 3, 4 };

Console.WriteLine("3 is in integer array: " + 3.IsIn(a));

var date = new DateTime(2009, 1, 2);

var dt = DateTime.Now.AddMilliseconds(-1);

var dates = new[] { new DateTime(2009,1,3), new DateTime(2005,2,6), date, DateTime.Now, dt};

Console.WriteLine("date {0:d} is in dates: {1}", date, date.IsIn(dates));

Console.WriteLine("date {0:d} is in dates: {1}", DateTime.Now, DateTime.Now.IsIn(dates));

Console.WriteLine("date {0:d} is in dates: {1}", dt, dt.IsIn(dates));

}

Попробуйте мысленно проиграть этот фрагмент кода и вычислить производимый им вывод. Объясните, почему результат вызова DateTime.Now.IsIn(dates) зависит от того, как он выполняется (по шагам, или нет). В тело метода Main() вручную введите 3. (точка) и оцените список доступных методов в подсказке Intellisense. Что выведет следующий фрагмент?

var a = new[] { 1, 2, 3, 4 };

var ints2D = new int[][] {

a,

new[] { 2, 3, 4 },

new[] { 1, 2, 3, 4, 5, 6, 7 }

};

Console.WriteLine("array {0:d} is in ints2D: {1}", a, a.IsIn(ints2D));

Проанализируйте синтаксис создания массива массивов и алгоритм поиска массива в массиве массивов.

Класс Extender содержит метод, расширяющий тип object, а, следовательно и все другие типы .NET Framework, так как все они происходят от object. Механизм расширений позволяет более избирательно подойти к этой проблеме и расширить любой другой (более специализированный, чем object) тип данных. Рассмотрим код статического класса, который расширяет все типы, реализующие интерфейс IList, а также тип object и string.

public static class Dumper

{

// ======= Первый аргумент extension-метода должен иметь описатель this

public static void Dump(this IList list) // Расширяем IList

{

foreach (object o in list)

o.Dump();

}

public static void Dump(this object o)

{

PropertyInfo[] properties = o.GetType().GetProperties();

foreach (PropertyInfo p in properties)

{

try

{

Console.WriteLine(string.Format("{0}  -->  {1}", p.Name, p.GetValue(o, null)));

}

catch

{

Console.WriteLine(string.Format("{0}  -->  {1}", p.Name, "unknown"));

}

}

}

public static void Show(this string what) { Console.WriteLine(what); }

}

Попытайтесь самостоятельно создать код для проверки этих методов. Применим созданное расширение для одиночного объекта класса string:

Console.WriteLine("\nTest Extension Method");

string s = "Now all the strings are extended with Dump and Show";

s.Dump();

s.Show();

В extension-методе Dump(this object o) мы опрашиваем все свойства произвольного объекта. Класс string имеет всего два свойства: индексатор Chars, трактуемый как свойство (property), и обычное свойство Length. Поэтому мы увидим следующий результат.