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

var persons = new List<Person>

{

new Person(){ Name = "Bale", Birth = new DateTime(1981, 3, 26), Salary = 10000m },

. . .

}

Generic-делегаты

Делегатом называется объект делегатного типа. Такой объект может ссылаться на метод (указывать на функцию в пределах какого-то класса). Объявления, начинающиеся словом delegate, декларируют делегатные типы (не делегаты). Они лишь задают сигнатуры методов (правила их использования), которые могут быть вызваны с помощью делегата. Говорят, что они задают сигнатуры возможных заданий делегатов. Сигнатурой метода называются правила его вызова, а именно: тип возвращаемого им значения и список типов его параметров.

В пространстве имен System (в сборке System.Core) появились встроенные делегатные типы: Action и Func, которые соответствует наиболее типичним сигнатурам методов. Рассмотрим тип Action. Он определен так:

public delegate void Action();    // Это определение уже существует в System.Core.dll

Любой метод с сигнатурой void() соответствует типу Action. Такой метод не требует аргументов и ничего не возвращает в точку вызова. Рассмотрим пример использования самого простого делегатного типа Action.

class Program

{

static void Inform() { Console.WriteLine(Environment.MachineName); }

static void Say() { Console.WriteLine("I am an Action delegate"); }

static void Main()

{

Action a = Inform;

a();

a += Say;

a();

}

}

¨  Action является делегатным типом, определенным в System.Core.

¨  Переменная a типа Action ссылается на метод Inform. Это возможно потому, что метод Inform соответствует сигнатуре делегатного типа Action. Переменная a является делегатом типа Action.

¨  Обращение к делегату: a(); эквивалентно прямому вызову метода Inform();.

¨  Оператор a += Say; добавляет в список заданий делегата a ссылку на метод Say. Следующий за этим оператором вызов делегата a(); выполнит два действия: Inform и Say. Это происходит потому, что список заданий делегата (InvocationList) теперь содержит две ссылки (на методы Inform и Say), а не одну, как было вначале.

Для иллюстрации сказанного вы можете (в рамках консольного проекта) выполнить следующий код:

class TestDelegate

{

public void Test()

{

Action a = new Action(Say);

a += new Action(Do);

Delegate[] dd = a.GetInvocationList();

Console.WriteLine("Testing Delegate's InvocationList\n");

int i = 0;

foreach (var d in dd)

Console.WriteLine("{0}. Method = {1}, CallingConvention = {2}, Target = {3}, Returns = {4}",

(++i).ToString(), d.Method.Name, d.Method.CallingConvention, d.Target,

d.Method.ReturnType.Name);

}

void Say() { Console.WriteLine("Saying"); }

void Do() { Console.WriteLine("Doing"); }

}

class Program

{

static void Main()

{

new TestDelegate().Test();

}

}

Запустив приложение, вы увидите такой результат.

Testing Delegate's InvocationList

1. Method = Say, CallingConvention = Standard, HasThis, Target = Test.TestDelegate, Returns = Void

2. Method = Do, CallingConvention = Standard, HasThis, Target = Test.TestDelegate, Returns = Void

В пространстве имен System существует делегатный тип Action<T>, который является универсальным (или, родовым — generic). Это означает, что он определяет не одну сигнатуру, а целое семейство (род) сигнатур.

public delegate void Action<T> (T obj); // Это определение тоже существует в System.Core.dll

Объявление Action<T> выглядит почти так же, как и шаблон функций в C++. Только там вместо ключевого слова delegate используется слово template. Доцент В.А. Биллиг предложил переводить термин generic, как универсальный (см. http://www.intuit.ru/department/pl/csharp/). 

Можно сказать, что generic-делегаты являются шаблонами ряда сходных сигнатур. Тип Action можно настроить с помощью параметра шаблона T. Например, объявление Action<int> определяет тип указателей на функции с сигнатурой void (int), а объявление Action<Person> настраивает шаблон на сигнатуру void (Person). Рассмотрим пример. Добавьте в класс Program еще одну версию метода Inform.