- •1. Жизненный цикл объекта.
- •2.Делегаты. Назначение, синтаксис объявления, использование делегата. Событийная модель.
- •3. События. Объявление события. Шаблон проектирования события в классе. Свойство для безопасной работы с событиями. Безопасный вызов события в многопоточном программировании.
- •4. Обобщенные классы. Объявление и детализация класса типами пользователя.
- •5. Обобщенные методы. Объявление и детализация методов типами пользователя.
- •6. Ограничения на параметры в обобщенных классах и функциях.
- •7. Типы, допускающие значение null
- •8. Коллекции и итераторы. Оператор yield
- •9.Потоки ввода/вывода в c#. Создание объекта потока и работа с ним. Текстовые, бинарные потоки, потоки со сжатием данных.
- •11. Исключения. Создание класса пользовательского исключения. Генерация и перехват исключения.
- •12. Рефлексия типов. Класс типа Type. Методы и свойства класса.
- •13. Сборки. Статическое и динамическое связывание сборок. Класс сборки Assembly.
- •14. Позднее связывание. Класс System.Activator. Создание экземпляра объекта путем позднего связывания. Вызов методов объекта путем позднего связывания.
- •15. Атрибуты. Назначение атрибутов. Базовый класс атрибута. Аннотирование класса атрибутами.
- •16. Расширяющие методы.
- •17. Анонимные методы. Объявление и использование.
- •18. Захват переменных анонимными методами. Цепочка вызовов.
- •20. Захват переменных в лямбда-выражениях. Рекурсивный вызов в лямбда-выражениях. Анонимная рекурсия.
- •21. Выражения и деревья выражений. Стандартные делегаты, используемые с выражениями и деревьями выражений.
- •22. Комбинирование выражений посредством класса InvocatedExpression.
- •23. Язык интегрированных запросов linq. Назначение, технические средства для реализации языка. Группы языка linq.
- •24. Linq to Objects. Вид запроса. Вызов цепочек расширяющих методов. Языковые конструкции для кодирования запроса. Отложенное и неотложенное выполнение запроса.
- •25. Конструкция from…select… Разделы конструкции. Условия. Проекция. Анонимные типы данных.
- •26. Конструкция from…group… Разделы конструкции. Условия. Ключи группировки. Получение ключа в результате-выборке.
- •28. Linq to Objects. Расширяющие методы для преобразования типов, операций со множествами, преобразования в один элемент.
- •29. Технология linq to sql. Назначение технологии. Класс контекста данных и основные атрибуты для создания контекста данных.
- •30. Создание и подключение класса контекста данных к базе данных. Шаблон программирования при добавлении данных в объект контекста данных.
20. Захват переменных в лямбда-выражениях. Рекурсивный вызов в лямбда-выражениях. Анонимная рекурсия.
Замыкания (захват переменной)
Анонимные методы могут захватывать контекст своего лексического окружения. Многие называют этот феномен захватом переменной. На языке функционального программирования это также известно как замыкание (closure). Рассмотрим простой пример замыкания в действии:
public class Closures
{
static void Main ()
{
int delta = 1;
Func<int, int> func = (x) => x + delta;
int currentVal = 0;
for( int i = 0; i < 10; ++i )
{
currentVal = func( currentVal );
Console.WriteLine( carrentVal );
}
}
}
Переменная delta и делегат func формируют замыкание. Тело выражения ссылается на delta и потому должно иметь доступ к ней при последующем выполнении. Чтобы обеспечить это, компилятор "захватывает" переменную для делегата. "За кулисами" же происходит вот что: тело делегата содержит ссылку на действительную переменную delta. Более того, поскольку захваченная переменная доступна как делегату, так и контексту, содержащему лямбда-выражение, это означает, что захваченная переменная может быть изменена вне контекста и вне связи с делегатом.
Одной из основ функционального программирования является то, что сама функция трактуется как первоклассный объект, которым можно манипулировать и оперировать, а также вызывать. Функции можно использовать в качестве строительных блоков для создания новых функций. Рассмотрим следующие два лямбда-выражения:
х => x*3;
x => x + 3.1415;
Можно создать метод для комбинирования таких лямбда-выражений с целью создания составного лямбда-выражения:
public class Compound
{
static Func<T,S> Chain<T, R, S> (Func<T,R> func1, Func<T,R> func2)
{
return x => func2 (func1(x));
}
static void Main ()
{
Func<int, double> func = Chain ((int x) => x*3, (int x) => x + 3.1415);
Console.WriteLine (func (2));
}
}
Метод Chain() принимает два делегата и производит третий делегат, комбинируя первые два. В методе Main он используется для производства составного выражения. Делегат, который мы получаем после вызова Chain(), эквивалентен делегату, который мы получаем при преобразовании следующего лямбда-выражения в делегат:
х => (х * 3) + 3.1415
Рассмотрим другие способы создания производных функций.
Построим операцию для вычисления n-го числа Фибоначчи:
public class Proof
{
static void Main ()
{
Func<int, int> fib = null;
fib = (x) => x > 1 ? fib(x-l) + fib(x-2) : x;
for (int i = 30; i < 40; ++i ) (
{ Console.WriteLine( fib(i) );}
}
}
Первое, что бросается в глаза при взгляде на этот код — формирование процедуры Фибоначчи, т.е. делегата fib. Он формирует замыкание на самом себе! Это определенно форма рекурсии, и она делает то, что надо. А именно, строит число Фибоначчи на основании предыдущих чисел.
Анонимная рекурсия. Рассмотрим замыкание, которое можно использовать для вычисления факториала числа:
Func<int, int> fact = null;
fact = (x) => x>l ? x* fact(x-l) : 1;
Этот код работает, потому что fact формирует замыкание на самом себе и также вызывает себя. То есть вторая строка, где fact присваивается лямбда-выражение для вычисления факториала, захватывает сам делегат fact. Невзирая на то, что замыкание захватывает переменную для использования внутри анонимного метода, который реализован здесь в виде лямбда-выражения, захваченная переменная остается доступной для изменения вне контекста захватывающего анонимного метода или лямбда-выражения.
Существует несколько способов решить эту проблему, но наиболее типичный заключается в использовании анонимной рекурсии.
Суть в том, что вместо рекурсии, полагающейся на захваченную переменную, являющуюся делегатом, мы передаем делегат рекурсии в качестве параметра. Теперь захваченная переменная находится в стеке, поскольку передается каждой рекурсии выражения. Однако, поскольку она в стеке, опасность модификации извне механизма рекурсии исключается.