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

Важно запомнить следующий факт. В коде функции, генерируемой компилятором, при реализации λ-выражения, доступны все локальные переменные того метода, где это выражение записано. Это свойство характеризуют термином — захват окружения.

Приведем еще три примера, иллюстрирующие работу с делегатом print типа Action.

Console.WriteLine("\n\nTest Action<string>");

Action<string> print = c => Console.WriteLine(c);

print("Hi from Action<string>");

___________________________________________________________________________

Console.WriteLine("\n\nEnum strings ");

string[] names = { "Microsoft", "creates", "technologies", "very", "quickly" };

Array.ForEach<string>(names, print);

___________________________________________________________________________

Console.WriteLine("\n\nLooking for code");

string[] codes = {

"Penal code", "Traffic code", "Morse code", "Genetic code",

"Coding rules", "Coding conventions", "Code names",

"Code of behaviour", "Code transmission", "Codex book", "Native code",

"Pseudocode", "Spaghetti code", "Portable code", "Native code",

"Scan code", "Access code", "Bar code", "Character code", "Country code"

};

Array.ForEach<string>(codes.Where(c => c.Contains("code")).ToArray(), print);

Generic-метод ForEach<>, расширяющий класс Array, мы рассморим немного позже. Но вы итак догадались, что это некий аналог оператора foreach, встроенного в язык C#. С помощью generic-метода ForEach и λ-выражения, можно получить форматированый вывод элементов массива. Например:

Console.WriteLine("\n\nFormat double as decimal");

var amounts = new double[] { 10.36, 12.0, 134 };

Array.ForEach<double>(amounts, c => Console.WriteLine("{0:c}", c));

Рассмотрим более сложный случай.

const string text =

@"Many types implement IEnumerable. Some of these types implement public

members that are identical to IEnumerable. For instance, if you have a

type MyList that implements a Where method (as does IEnumerable<T>),

invoking Where on MyList would call MyList’s implementation of Where.

By calling the AsEnumerable() method first and then calling Where,

you invoke IEnumerable’s Where method instead of MyList’s.";

List<string> words = new List<string>();

words.AddRange (text.Split());

Console.WriteLine("\n\nTest Query Where:");

var where = words.Where(c => c == "Where");

foreach (var s in where)

Console.Write(s + ", ");

Здесь все яcно, кроме вызова extension-метода Where, которого не было в предыдущей версии языка C#. Теперь такой метод обслуживает все списки, массивы и другие коллекции. Попытайтесь ответить на вопрос: Какой тип имеет переменная where? Компилятор вычислит его, исходя из контекста выражения:

var where = words.Where (c => c == "Where");

Простым решением будет посмотреть подсказку Intellisense при наведении курсора на метод Where. Она сообщает, что типом возвращаемого значения будет IEnumerable<string>. Таким образом, это выражение можно заменить на эквивалентное ей, но не использующее описатель var (анонимного типа).

IEnumerable<string> where = words.Where(c => c == "Where");

Задача. Определить сигнатуру анонимного делегата, генерируемого на основе λ-выражения:

c => c == "Where"

Ответ. Делегат имеет тип: Func<string, bool>, так как параметр c имеет тип string, а выражение c == "Where" имеет тип bool.

При изучении LINQ вам придется работать с еще более сложными типами generic-коллекций, чем рассмотреный нами IEnumerable<T>. Компилятор конструирует такие типы автоматически и в большом количестве, поэтому, введение описателя var следует воспринимать, как средство, увеличивающее продолжительность нашей жизни.