Try-catch-finally 13 страница
Список вызова делегата, созданного на базе анонимной функции, содержит одну запись. Конечные объект и метод делегата не определены. В частности, не определено, является ли конечный объект делегата объектом null, а также значение this включающей функции-члена или любого другого объекта. Допускаются (но не являются обязательными) преобразования семантически идентичных анонимных функций с одинаковым (возможно пустым) набором экземпляров внешних переменных к одному типу делегата. В результате таких преобразований возвращается один экземпляр делегата. Термин «семантически идентичные» означает, что выполнение анонимных функций с одинаковыми аргументами во всех случаях дает одинаковые результаты. Это правило позволяет оптимизировать код, аналогичный приведенному ниже. delegate double Function(double x); class Test static void F(double[] a, double[] b) { Поскольку делегаты двух анонимных функций имеют одинаковые (возможно пустые) наборы внешних записанных переменных, а сами функции являются семантически идентичными, при компиляции делегаты могут ссылаться на один конечный метод. Соответственно, компилятор может возвращать одинаковые экземпляры делегата по результатам выполнения выражений обеих анонимных функций. 6.5.2 Вычисление преобразования анонимной функции к типу дерева выражений В результате преобразования анонимной функции к типу дерева выражений создается дерево выражений (§4.6). Точнее говоря, в результате вычисления преобразования анонимной функции создается объектная структура, представляющая саму функцию. Точная структура дерева выражений, а также процесс его построения указываются при реализации. 6.5.3 Пример реализации; В данном разделе описывается возможная реализация преобразования анонимных функций в терминах других конструкций C#. Приведенная реализация базируется на принципах, используемых компилятором Microsoft C#, и не является ни обязательной, ни единственно возможной. Здесь приводится лишь краткое описание преобразований к дереву выражений, поскольку полная семантика таких преобразований в данной спецификации не рассматривается. Далее в разделе рассматривается несколько примеров кода, содержащих анонимные функции с различными характеристиками. Для каждого примера представлен соответствующий код, в котором используются только другие конструкции C#. В данных примерах идентификатор D представляет следующий тип делегата: public delegate void D(); Анонимная функция простейшего вида не записывает внешние переменные: class Test Это можно преобразовать в код создания экземпляра делегата, ссылающегося на создаваемый компилятором статический метод, в который помещается код анонимной функции: class Test static void __Method1() { В следующем примере анонимная функция ссылается на члены экземпляра this: class Test void F() { Это можно преобразовать в создаваемый компилятором метод экземпляра, содержащий код анонимной функции: class Test void F() { void __Method1() { В этом примере анонимная функция записывает локальную переменную: class Test Время существования локальной переменной необходимо увеличить как минимум до времени существования делегата анонимной функции. Для этого можно поместить (поднять) локальную переменную в поле создаваемого компилятором класса. В этом случае создание экземпляра локальной переменной (§7.15.5.2) будет соответствовать созданию экземпляра класса компилятором. Обращение к локальной переменной будет соответствовать обращению к полю экземпляра создаваемого компилятором класса. Кроме того, анонимная функция становится методом экземпляра создаваемого компилятором класса: class Test class __Locals1 public void __Method1() { Следующая анонимная функция записывает this, а также две локальные переменные с различным временем существования: class Test void F() { Здесь компилятором создается класс для каждого блока оператора, в котором выполняется запись локальных переменных. За счет этого локальные переменные в каждом блоке имеют различное время существования. Экземпляр класса __Locals2, создаваемого компилятором для внутреннего блока оператора, включает в себя локальную переменную z и поле, содержащее ссылку на экземпляр __Locals1. Экземпляр класса __Locals1, создаваемого компилятором для внешнего блока оператора, включает в себя локальную переменную y и поле, содержащее ссылку на объект this включающей функции-члена. При такой структуре данных с помощью экземпляра __Local2 возможно обращение ко всем записанным внешним переменным. Таким образом, код анонимной функции реализуется как метод экземпляра этого класса. class Test class __Locals1 class __Locals2 public void __Method1() { Способ, аналогичный применяемому здесь для записи локальных переменных, может использоваться при преобразовании анонимных функций к деревьям выражений. Ссылки на создаваемые компилятором объекты могут храниться в дереве выражений, а обращение к локальным переменным может быть представлено как обращение к полям этих объектов. Преимущество такого подхода заключается в том, что локальные переменные с нулификацией (поднятые) могут совместно использоваться делегатами и деревьями выражений.
|