Visual Basic, .NET, ASP, VBScript
 

   
 
Описание для автора не найдено
 
     
   
 
Будущие возможности языка программирования C#
Автор: Prashant Sridharan, Корпорация Microsoft (http://www.microsoft.com)
Перевод: Шатохина Надежда(sna@uneta.org), Ukraine .Net Alliance (http://www.uneta.org)
Март 2003
Применяется к:
  • Microsoft® Visual C#™
  • Обзор:
    Корпорация Microsoft разрабатывает следующую основную версию языка программирования C#. В этой статье приведена информация, касающаяся четырех новых ключевых возможностей: шаблоны, итераторы, анонимные методы и неполные (partial) типы.
    Содерджание:
  • Введение
  • Шаблоны
  • Итераторы
  • Анонимные методы
  • Неполные типы
  • Стандартизация
  • Доступность
  • Дополнительная информация
  • Введение

    C# - это современный и прогрессивный язык программирования, который включает возможности, доступные в наиболее распространенных промышленных и исследовательских языках. Придерживаясь философии разработки С#, Microsoft ввела в него несколько потенциально новых возможностей, увеличивающих производительность разработчика с помощью структурных компонентов языка.

    Microsoft C#

    Начиная с официального представления в феврале 2001 некоторые разработчики создают программное обеспечение, используя язык программирования С#. Даже в самой Microsoft C# использовался для построения нескольких поставляемых приложений, включая .NET Framework, MSN Web свойства и Tablet PC SDK. По существу, C# доказал, что является языком, пригодным для создания высококачественного коммерческого программного обеспечения.

    Многие из возможностей языка программирования C# были созданы для четырех различных целей:

    • Единая система типов и упрощение способов использования языком значений и ссылочных типов.
    • Разработка на основе компонентов, основанная на таких возможностях как XML комментарии, атрибуты, свойства, события и делегаты.
    • Практические преимущества основанные на уникальных возможностях языка C#, включая работу с безопасными указателями, контроль переполнения и т.д.
    • Удобные языковые конструкции, такие как операторы foreach и using, повышающие производительность разработчика.

    В версии "Visual Studio for Yukon" языка C# Microsoft планирует расширить и без того изящный и выразительный синтаксис, включая разнообразные возможности через широкий спектр исследовательских и промышленных языков. Среди этих возможностей – шаблоны, итераторы, анонимные методы и неполные типы.

    Будущие потенциальные возможности

    В самом деле, эти нововведения в C# основываются на единой системе типов, разработке на основе компонентов, практических преимуществ и удобных языковых конструкциях. Далее рассматриваются четыре новых ключевых возможности, которые Microsoft планирует поставлять в следующей основной версии языка программирования C#. Разработка этих возможностей еще не завершена, и Корпорация Microsoft приглашает сообщество разработчиков к дискуссии на эту тему.

    Шаблоны

    По мере усложнения проектов программисты все больше и больше нуждаются в средствах повторного использования и переделки существующего базирующегося на компонентах программного обеспечения. Чтобы достигнуть такого высокого уровня повторного использования кода в других языках программирования, разработчики обычно применяют возможность, называемую шаблонами. В C# будет включена высокопроизводительная версия шаблонов с безопасными типами, мало отличающаяся в синтаксисе и сильно отличающаяся в реализации от шаблонов, применяемых в C++, и шаблонов, предлагаемых для языка программирования Java.

    Построение шаблонных классов сегодня

    Сегодня в C# программисты могут создавать ограниченные версии действительно шаблонных типов, сохраняя данные в экземплярах типа шаблонного объекта. Поскольку каждый объект в C# наследуется от типа шаблонного объекта и есть возможность упаковки и распаковки общей системы типов .NET, программисты могут сохранять и ссылки, и типы значений в переменной типа object. Однако при преобразованиях между ссылочными типами, типами значений и типом шаблонного объекта возникают потери производительности.

    Проиллюстрируем это следующим примером кода, в котором создается простой тип Stack с двумя операциями: "Push" и "Pop". Класс Stack сохраняет свои данные в массиве типов object, методы Push и Pop используют базовый тип object, чтобы принимать и возвращать данные, соответственно:

    public class Stack
    {
       private object[] items = new object[100];
    
       public void Push(object data)
       {
          ...
       }
    
       public object Pop()
       {
          ...
       }
    }			
    			

    Затем произвольный тип — тип Customer, например, — может быть добавлен в стек. Однако, если ваша программа должны извлекать данные, необходимо явно привести результат метода Pop, типа базового объекта, к типу Customer.

    Stack s = new Stack();
    s.Push(new Customer());
    Customer c = (Customer) s.Pop();			
    			

    Если в метод Push передается тип значения, например, integer, среда выполнения автоматически преобразовывает его в ссылочный тип – процесс, известный как упаковка – и затем сохраняет его во внутренней структуре данных. Точно так же, если вы хотите, чтобы программа извлекала из стека тип значений, такой как integer, ей необходимо явно преобразовывать тип объекта, полученный из метода Pop, в тип значения, этот процесс известен как распаковка:

    Stack s = new Stack();
    s.Push(3);
    int i = (int) s.Pop();			
    			

    Операции упаковки и распаковки между типами значений и ссылочными типами на практике могут быть довольно тяжелы в плане производительности.

    К тому же, в текущей реализации невозможно задать вид данных, помещаемых в стек. Конечно стек может быть создан и затем тип Customer может быть помещен в него. Позже этот же стек может использоваться и пытаться получить данные и преобразовать их в другой тип, как в следующем примере:

    Stack s = new Stack();
    s.Push(new Customer());
    
    Employee e = (Employee) s.Pop();			
    			

    Однако, в то время как предыдущий код является примером неправильного использования одного типа класса Stack, который нуждается в дополнительной реализации и должна возникнуть ошибка, это действительно допустимый код и компилятор не будет иметь проблем с ним. Во время выполнения программа все-таки даст сбой из-за неправильной операции преобразования.

    Создание и использование шаблонов

    В C# шаблоны обеспечивают возможности создания высокопроизводительных структур данных, которые специализируются компилятором на основе используемых ими типах данных. Эти, так называемые, параметризованные типы создаются таким образом, что их внутренние алгоритмы остаются теми же, но такими, что типы их внутренних данных могут различаться в зависимости от предпочтений конечного пользователя.

    Чтобы минимизировать затраты на обучение для разработчиков, шаблоны в C# объявляются в основном так же, как и в C++. Программисты могут создавать классы и структуры точно так же, как они обычно это делали, а используя угловые скобки (< and >) можно определить параметры типа. При использовании класса каждый параметр должен быть замещен реальным типом, поставляемым пользователем класса.

    В следующем примере создайте класс Stack там, где определен и объявлен в угловых скобках после объявления класса параметр типа ItemType. Вместо того чтобы производить преобразования в и из типа базового объекта, экземпляры базового класса Stack примут тип, для которого они созданы. Параметр типа ItemType работает как промежуточный элемент до тех пор, пока этот тип определен во время реализации и используется как тип для внутреннего массива элементов – тип для параметра метода Push и возвращаемый тип метода Pop:

    public class Stack<ItemType>
    {
       private ItemType[] items;
    
       public void Push(ItemType data)
       {
          ...
       }
    
       public ItemType Pop()
       {
          ...
       }
    }			
    			

    Когда ваша программа использует класс Stack, как в следующем примере, вы можете определить действительный тип, предназначенный для использования шаблонным классом. В этом случае вы указываете классу Stack использовать примитивный тип integer, определяя его как параметр, используя угловую нотацию в выражении реализации:

    Stack<int> stack = new Stack<int>();
    stack.Push(3);
    int x = stack.Pop();			
    			

    Таким образом, ваша программа создает новый экземпляр класса Stack, для которого каждый ItemType замещен поставляемым integer параметром. Конечно, когда ваша программа создает новый экземпляр класса Stack с integer параметром, внутренний массив элементов в классе Stack теперь integer, а не object. Кроме того, ваша программа устранила потери на упаковку, связанные с помещением integer в стек. Более того, когда ваша программа получает элемент из стека, вам больше не надо явно преобразовывать его в соответствующий тип, потому что этот конкретный экземпляр класса Stack использует integer в своей структуре данных.

    Если вы хотите, чтобы ваша программа сохраняла в классе Stack элементы, отличные от integer, вы должны создать новый экземпляр класса Stack, определяя новый тип как параметр. Предположим, вы имеете простой тип Customer и хотите, чтобы программа для его сохранения использовала объект Stack. Чтобы сделать так, просто реализуйте классы Stack с объектом Customer в качестве параметра типа и свободно повторно используйте код вашей программы:

    Stack<Customer> stack = new Stack<Customer>();
    stack.Push(new Customer());
    Customer c = stack.Pop();			
    			

    Конечно, как только ваша программа создает класс Stack с типом Customer в качестве параметра, она имеет возможность сохранять только типы Customer в стеке. В C# шаблоны строго типизированы, что означает, что вы не можете больше неправильно сохранять integer в стеке, как это сделано в следующем примере:

    Stack<Customer> stack = new Stack<Customer>();
    stack.Push(new Customer());
    stack.Push(3)  // compile-time error
    Customer c = stack.Pop();      // no cast required.			
    			

    Преимущества шаблонов

    Шаблоны дают возможность программистам создавать, тестировать и распространять код однажды, а затем повторно его использовать для различных типов данных. В то время как это истинно для первого примера Stack, второй пример Stack позволяет вашей программе повторно использовать код с незначительными потерями производительности приложений. Для типов значений первый пример Stack приводит к существенным проблемам с производительностью, тогда как второй пример Stack полностью устраняет проблемы, потому что вы ликвидировали упаковку и нисходящие приведение типов.

    Кроме того, шаблоны проверяются во время компилирования. Когда ваша программа создает экземпляр шаблонного класса с предоставленным параметром типа, он может быть только того типа, который определила ваша программа при описании класса. Например, после того как программа создала Stack объектов Customer, она больше не могла помещать integer в стек. Используя такое поведение, вы можете построить более надежный код.

    Более того, реализация шаблонов в C# по сравнению с другими реализациями со строгими типами сокращает разрастание кода. Создавая типизированные коллекции с помощью шаблонов, вы можете избежать необходимости создания специальных версий каждого класса, сохраняя при этом преимущества в производительности, возникающие в случае создания таких версий. Например, ваша программа может создать один параметризованный класс Stack и избежать необходимости создания IntegerStack для сохранения integers, StringStack для сохранения строк или CustomerStack для сохранения типов Customer.

    Это, конечно, приводит к созданию более надежного кода. Создавая один класс Stack, ваша программа может инкапсулировать все поведение, связанное со стеком, в один удобный класс. Затем, когда вы создаете Stack типов Customer, все еще очевидно, что ваша программа использует стековую структуру данных, хотя и с сохраненными в ней типами Customer.

    Множество параметров типа

    Шаблонные типы могут иметь любое количество типов параметров. В предыдущем примере Stack использовался только один тип. Предположим, вы создали простой класс Dictionary, который сохраняет значения вместе с ключами. Ваша программа может определить шаблонную версию класса Dictionary объявляя два параметра, разделенные запятыми в угловых скобках описания класса.

    public class Dictionary<KeyType, ValType>
    {
       public void Add(KeyType key, ValType val)
       {
          ...
       }
    
       public ValType this[KeyType key]
       {
          ...
       }
    }			
    			

    При использовании класса Dictionary вы должны поставлять множество параметров в угловых скобках выражения реализации, вновь разделенные запятыми, и обеспечивать правильные типы параметрам функции Add и индексатору:

    Dictionary<int, Customer> dict = new Dictionary<int, Customer>();
    dict.Add(3, new Customer());
    Customer c = dict.Get[3];			
    			

    Ограничения

    Как правило, ваша программа будет делать больше, чем просто сохранять данные на основе данного параметра типа. Зачастую вам захочется, чтобы ваша программа использовала члены параметра типа для того, чтобы выполнять операторы в шаблонном типе вашей программы.

    Почему нужны ограничения

    Предположим, что в методе Add класса Dictionary вы хотите сравнивать элементы, используя метод CompareTo предоставленного ключа, например:

    public class Dictionary<KeyType, ValType>
    {
       public void Add(KeyType key, ValType val)
       {
          ...
          switch(key.CompareTo(x))
          {
          }
          ...
       }
    }			
    			

    К сожалению, во время компиляции параметр KeyType, как ожидалось, не определен. Написанный таким образом код, компилятор полагает, что ключевому экземпляру параметра типа KeyType доступны только операции, доступные типу шаблонного объекта, такие как ToString. В результате компилятор выводит ошибку, потому что метод CompareTo не определен. Тем не менее ваша программа может преобразовать ключевую переменную в объект, содержащий метод CompareTo, такой как интерфейс IComparable. В следующем примере ваша программа явно преобразовывает экземпляр типа параметра KeyType, называемого ключом, в интерфейс IComparable, что позволяет откомпилировать программу:

    public class Dictionary<KeyType, ValType>
    {
       public void Add(KeyType key, ValType val)
       {
          ...
          switch(((IComparable) key).CompareTo(x))
          {
          }
          ...
       }
    }			
    			

    Однако, если теперь вы обрабатываете класс Dictionary и поставляете параметр типа, который не реализует интерфейс IComparable, возникнет ошибка времени выполнения, а именно InvalidCastException.

    Объявление ограничений

    В C# ваша программа может поставлять дополнительный список ограничений для каждого параметра типа, объявленного в шаблонном классе. Ограничения отображают требования, которым должен удовлетворить тип для того, чтобы создать шаблонный тип. Ограничения объявляются с помощью ключевого слова where, за которым следует пара "параметр-требование", в которой "параметром" должен быть один из параметров, определенных в шаблонном типе, а "требование" должно быть классом или интерфейсом.

    Для того, чтобы удовлетворить необходимости использования метода CompareTo в классе Dictionary, ваша программа может накладывать ограничение на параметр типа KeyType, требуя, чтобы в первый параметр класса Dictionary для реализации интерфейса IComparable передавался любой тип, например:

    public class Dictionary<KeyType, ValType> where KeyType : IComparable
    {
       public void Add(KeyType key, ValType val)
       {
          ...
          switch(key.CompareTo(x))
          {
          }
          ...
       }
    }			
    			

    Теперь после компиляции ваш код будет проверен для подтверждения того, что всякий раз при использовании класса Dictionary ваша программа передает в качестве первого параметра тип, реализующий интерфейс IComparable. Кроме того, вашей программе больше не надо явно преобразовывать переменную в интерфейс IComparable перед вызовом метода CompareTo.

    Множественные ограничения

    Для любого данного параметра типа ваша программа может определить в качестве ограничений любое количество интерфейсов, но не более чем один класс. Каждое новое ограничение объявляется как еще одна пара параметр-требование; каждое ограничение для данного шаблонного типа отделяется запятыми. В следующем примере класс Dictionary содержит два типа параметров: KeyType и ValType. Параметр типа KeyType имеет два интерфейса-ограничения, в то время как у параметра типа ValType один класс-ограничение:

    public class Dictionary<KeyType, ValType> where
    KeyType : IComparable,
    KeyType : IEnumerable,
    ValType : Customer
    {
       public void Add(KeyType key, ValType val)
       {
          ...
          switch(key.CompareTo(x))
          {
          }
          ...
       }
    }
    			

    Шаблоны в среде выполнения

    Когда шаблонный класс откомпилирован, между ним и обычным классом фактически нет никакой разницы. На самом деле, результатом компиляции является не что иное как метаданные и промежуточный язык (IL). IL, конечно же, параметризован, чтобы принимать поставляемые пользователем типы где-то в коде. Отличие в использовании IL для шаблонного типа основано на том, является ли поставляемый параметр типа типом значения или ссылочным типом.

    При первичном создании шаблонного типа с типом значения в качестве параметра, среда выполнения создает специализированный шаблонный тип с предоставленным параметром (или параметрами), подставленными в соответствующие места в IL. Специализированные шаблонные типы создаются единожды для каждого уникального типа значения, используемого в качестве параметра.

    Например, предположим в коде вашей программы объявлен Stack, построенный из integers:

    Stack<int> stack;			
    			

    На этом этапе среда выполнения генерирует специализированную версию класса Stack с целыми, подставленными соответственно его параметру. Теперь, когда бы код вашей программы ни использовал Stack целых, среда выполнения вновь и вновь использует сгенерированный специализированный класс Stack. В следующем примере созданы два экземпляра Stack целых, оба используют уже сгенерированный средой выполнения код для Stack целых:

    Stack<int> stackOne = new Stack<int>();
    Stack<int> stackTwo = new Stack<int>();			
    			

    Однако, если в другой точке кода вашей программы создается другой класс Stack, на этот раз с другим типом значения, таким как long или определенной пользователем структурой в качестве параметра, среда выполнения генерирует другую версию шаблонного типа, на этот раз вставляя long в соответствующие места IL. Преимуществом создания специализированных классов для шаблонных конструкций с типами значений является лучшая производительность. Как-никак, больше не нужны преобразования, потому что каждый специализированный шаблонный класс содержит тип значения.

    Немного иначе работают шаблоны для ссылочных типов. Как только впервые создается шаблонный тип с любым ссылочным типом, среда выполнения создает специализированный шаблонный тип с объектными ссылками, подставленными для параметров в IL. Затем каждый раз при создании экземпляра построенного типа с ссылочным типом в качестве параметра в зависимости от того, какой это тип, среда выполнения вновь и вновь использует ранее созданную специализированную версию шаблонного типа.

    Например, предположим у вас есть два ссылочных типа, классы Customer и Order, и далее предположим, что вы создали Stack типов Customer:

    Stack<Customer> customers; 			
    			

    На этом этапе среда выполнения генерирует специализированную версию класса Stack, которая вместо сохранения данных сохраняет объектные ссылки, которые будут заполняться позже. Предположим, следующая строка кода создает Stack другого ссылочного типа, названного Order:

    Stack<Order> orders = new Stack<Order>();			
    			

    В отличие от типов значений, для типа Order не создается другая специализированная версия класса Stack. Экземпляр специализированной версии класса Stack создается и задаются ссылки на нужные объекты. Для каждой объектной ссылки, которая была подставлена на место параметра типа, выделяется область памяти размером с тип Order и устанавливается указатель на эту ячейку памяти. Предположим, что затем вы добавили строку кода, чтобы создать Stack типа Customer:

    customers = new Stack<Customer>();			
    			

    Как и при предыдущем использовании класса Stack, созданного с типом Order, создается другой экземпляр специализированного класса Stack, и содержащиеся в нем указатели ссылаются на область памяти размером с тип Customer. Т.к. от программы к программе количество ссылочных типов может широко варьироваться, реализация шаблонов в C# сильно уменьшает количество кода, сокращая до одного количество специализированных классов, создаваемых компилятором для шаблонных классов ссылочных типов.

    Более того, когда создается экземпляр шаблонного класса C# с параметром типа, являющимся типом значения или ссылочным типом, во время выполнения он может запрашиваться, используя reflection и оба его действительных типа, так же как может быть установлен его параметр типа.

    Различия между C# шаблонами и другими реализациями

    Шаблоны С++ существенно отличаются от C# шаблонов. Там где C# шаблоны компилируются в IL, что приводит к тому, что для каждого типа значения специализация разумно происходит во время выполнения и однажды только для ссылочных типов, шаблоны С++ являются по существу макросом расширения кода, которые генерируют специализированный тип для каждого параметра типа, поставляемого в шаблон. Кроме того, когда компилятор С++ сталкивается с шаблоном, таким как Stack целых, он расширяет код шаблона в класс Stack, содержащий целые как собственный тип. В зависимости от того, является ли параметр типа типом значения или ссылочным типом, за исключением компоновщика, специально разработанного для уменьшения количества кода, каждый раз компилятор С++ создает специализированный класс, в результате чего возникает существенное увеличение размера кода в сравнении C# шаблонами.

    Более того, шаблоны С++ не могут определять ограничения. Они могут только неявно задать ограничения, просто используя член, который должен или не должен принадлежать параметру типа. Если в параметре типа, который в итоге передается в шаблонный класс, этот член существует, программа будет работать правильно. Если член не существует, программа даст сбой и, возможно, будет возвращено непонятное сообщение об ошибке. Поскольку C# шаблоны могут объявлять ограничения и строго типизированы, таких потенциальных ошибок нет.

    Между тем, компания Sun Microsystems® предложила дополнение к шаблонам в следующей версии языка программирования Java под кодовым названием "Tiger". Компания Sun выбрала реализацию, которая не требует изменения Виртуальной машины Java.

    Предложенная Java реализация использует синтаксис, подобный шаблонам в С++ и шаблонов в C#, включая параметры типов и ограничения. Однако из-за того, что типы значений и ссылочные типы интерпретируются по-разному, немодифицированная Виртуальная машина Java не сможет поддерживать шаблоны для типов значений. По существу, применения шаблонов в Java не приведет к увеличению эффективности выполнения. Действительно, компилятор Java будет вводить автоматические приведение типов из указанного ограничения, если оно объявлено, или базового типа Object, если ограничение не объявлено, всякий раз, когда необходимо будет возвращать данные. Более того, компилятор Java будет генерировать отдельный специализированный тип во время компиляции, который затем будет использовать для обработки любого созданного типа. И в заключение, т.к. Виртуальная машина Java изначально не поддерживает шаблонов, не будет способа обнаружить параметр типа для экземпляра базового типа во время выполнения, и другие применения reflection будут жестко ограничены.

    Поддержка шаблонов в других языках

    У компании Microsoft есть намерение поддержать использование и создание шаблонных типов в Visual J#™, Visual C++ и Visual Basic. В то время как некоторые языки программирования могут реализовать эту возможность раньше, чем другие, все три другие языка программирования Microsoft будут включать поддержку шаблонов. Тем временем команда C# закладывает фундамент для возможности многоязыковой поддержки. Microsoft тесно сотрудничает с партнерами для обеспечения создания и использования шаблонов в языках программирования .NET.

    Итераторы

    Итератор является языковой конструкцией, основанной на подобных возможностях в таких исследовательских языках, как CLU, Sather и Icon. Итераторы облегчают типам задание оператора foreach.

    Зачем нужны итераторы

    Сегодня, если классы хотят поддерживать итерирование, используя конструкцию цикла foreach, они должны реализовать "enumerator" шаблон. Например, конструкция цикла foreach, приведенная слева, расширена компилятором в конструкцию цикла while, приведенную справа:

    Конструкция цикла foreach Конструкция цикла while
    List list = ...;
    foreach(object obj in list)
    {
    DoSomething(obj);
    }
    
    Enumerator e = list.GetEnumerator();
    while(e.MoveNext())
    {
       object obj = e.Current;
       DoSomething(obj);
     }
    

    Таблица 1.

    Обратите внимание, что структура данных List – итерируемый элемент – должна поддерживать функцию GetEnumerator , для того чтобы работал цикл foreach. Теперь, когда создана структура данных List, должна быть реализована функция GetEnumerator, которая затем возвратит объект ListEnumerator:

    public class List
    {
       internal object[] elements;
       internal int count;
    
       public ListEnumerator GetEnumerator()
       {
          return new ListEnumerator(this);
       }
    }			
    			

    Созданный объект ListEnumerator должен не только реализовывать свойство Current и метод MoveNext, но и также управлять своим внутренним состоянием таким образом, чтобы программа в ходе цикла могла каждый раз перемещаться на следующий элемент. Эта машина внутреннего состояния может быть простой для структуры данных List, но для структур данных, которые требуют рекурсивного прохождения, таких как бинарные деревья, машина состояния может быть достаточно сложной.

    Т.к. реализация этого «enumerator» шаблона может потребовать больших затрат и написания большого количества кода со стороны разработчика, C# будет включать новую конструкцию, которая облегчит классу реализацию логики цикла foreach.

    Определение итератора

    Поскольку итератор – это логический аналог конструкции цикла foreach, он задается подобно функции, с помощью ключевого слова foreach, за которым следует пара круглых скобок. В следующем примере ваша программа объявит итератор для типа List. Возвращаемый итератором тип определяется пользователем, но поскольку класс List сохраняет внутри объектный тип, следующего примера итератора будет возвращать объект:

    public class List
    {
       internal object[] elements;
       internal int count;
    
       public object foreach()
       {
       }
    }			
    			

    Обратите внимание, что как только реализован «enumerator» шаблон, вашей программе надо поддерживать машину внутреннего состояния, для того чтобы отслеживать местоположение программы в структуре данных. У итераторов есть встроенные машины состояний. Используя новое ключевое слово yield, ваша программа может возвращать значения в оператор foreach, вызвавший итератор. В следующий раз, когда снова вызывает итератор, он начинает выполнение с того места, на котором остановился предыдущий оператор yield. В следующем примере ваша программа итерирует по трем строковыым типам:

    public class List
    {
       internal object[] elements;
       internal int count;
    
       public string foreach()
       {
          yield "microsoft";
          yield "corporation";
          yield "developer division";
       }
    }			
    			

    В следующем примере цикл foreach, который вызывает этот итератор, будет выполнен три раза, каждый раз получая строки в порядке, определенном тремя предыдущими операторами yield:

    List list = new List();
    foreach(string s in list)
    {
    Console.WriteLine(s);
    }			
    			

    Если вы хотите, чтобы программа реализовывала итератор для прохода элементов списка, вы должны модифицировать итератор так, чтобы он проходил через массив элементов, используя цикл foreach, возвращая каждый элемент массива в каждой итерации:

    public class List
    {
       internal object[] elements;
       internal int count;
    
       public object foreach()
       {
          foreach(object o in elements)
          {
             yield o;
          }
       }
    }			
    			

    Как работают итераторы

    Итераторы выполняют рутинную работу по реализации «enumerator» шаблона от имени вашей программы. Вместо того, чтобы создавать классы и машину состояния, компилятор C#, используя «enumerator» шаблон, преобразовывает написанный в вашем итераторе код в соответствующие классы и код. Таким образом, итераторы обеспечивают существенное увеличение продуктивности разработчика.

    Анонимные методы

    Анонимные методы – это другая практическая языковая конструкция, которая дает возможность программистам создавать блоки кода, которые могут быть инкапсулированы в делегаты и выполняться позже. Они основываются на языковой концепции, называемой лямбда функцией, и подобны конструкциям, используемым в Lisp и Python.

    Создание кода делегата

    Делегат – это объект, ссылающийся на метод. Где бы ни вызывался делегат, вызывается и метод, на который он ссылается. В следующем примере показана форма с тремя элементами управления – окно списка (list box), текстовое окно (text box) и кнопка (button). Когда инициализируется кнопка, программа приказывает ее делегату Click обратиться к методу AddClick, хранящемуся где-то в объекте. В методе AddClick значение текстового окна сохраняется в окне списка. В силу того, что метод AddClick добавлен в делегат Click экземпляра кнопки, он вызывается всякий раз при нажатии кнопки.

    public class MyForm
    {
       ListBox listBox;
       TextBox textBox;
       Button button;
    
       public MyForm()
       {
    listBox = new ListBox(...);
    textBox = new TextBox(...);
    button = new Button(...);
    button.Click += new EventHandler(AddClick);
    }
    
       void AddClick(object sender, EventArgs e)
       {
          listBox.Items.Add(textBox.Text);
       }
    }			
    			

    Использование анонимных методов

    Предыдущий пример довольно прямой. Создается отдельная функция, на нее ссылается делегат, и где бы не вызывался делегат, программа вызывает функцию. Внутри функции осуществляются серии выполняемых шагов. С добавлением анонимных методов ваша программа может отказаться от создания совершенно нового метода для класса, а вместо этого прямо ссылаться на выполняемые шаги, содержащиеся в делегате.

    В следующем примере ваша программа заменяет оператор создания делегата прямым вызовом необходимых шагов.

    public class MyForm
    {
       ListBox listBox;
       TextBox textBox;
       Button button;
    
       public MyForm()
       {
    listBox = new ListBox(...);
    textBox = new TextBox(...);
    button = new Button(...);
    button.Click += new EventHandler(sender, e)
    {
             listBox.Items.Add(textBox.Text);
    };
    }
    }			
    			

    Обратите внимание на то, как код в пределах анонимного метода может доступаться и манипулировать переменными, объявленными вне его границ. Действительно, анонимные методы могут обращаться к переменным, объявленным как классом, так и параметрами, или локальным переменным, объявленным в методе, в котором он.

    Передача параметров в анонимные методы

    Любопытно, что анонимный метод принимает два параметра, называемых sender и e. Посмотрев на определение делегата Click класса Button, вы обнаружите, что любая функция, на которую ссылается делегат, должна включать два параметра; тип первого - объект, второго - EventArgs. В первом примере, без использования анонимных методов, ваша программа передает два параметра в метод AddClick, объект и EventArgs.

    Даже если код написан непосредственно, делегат должен получать два параметра. В анонимном методе имена двух параметров должны быть объявлены таким образом, чтобы ассоциированный блок мог использовать их. Когда происходит событие Click, вызывается анонимный метод и в него передаются соответствующие параметры.

    Как работают анонимные методы

    Когда встречается делегат анонимного метода, компилятор C# автоматически преобразовывает код в его границах выполнения в функцию с уникальным именем в рамках класса с уникальным именем. Ссылки делегат, в котором хранится блок кода, затем устанавливаются на сгенерированные компилятором объект и метод. При вызове делегата блок анонимного метода выполняется с помощью сгенерированного компилятором метода.

    Неполные типы

    В то время, как хорошей практикой объектно-ориентированного программирования является сохранение всего исходного кода типа в одном файле, иногда ограничения производительности вынуждают отступать от этого правила. Кроме того, цена разделения типа на подтипы не приемлема для всех ситуаций. Более того, программисты предлагают создавать или использовать приложения, которые выпускают исходный код, модифицируя результирующий код. К сожалению, когда однажды в будущем выпускается исходный код, все существующие модификации исходного кода переписываются.

    Неполные типы дают вам возможность разбить типы, состоящие из большого количества исходного кода, на несколько различных исходных файлов для облегчения разработки и обслуживания. Более того, неполные типы позволяют разделить части типов, сгенерированные машиной и написанные пользователем, таким образом облегчая дополнение или изменение кода, сгенерированного инструментом.

    В следующем примере оба файла кода C#, File1.cs и File2.cs, содержат описания класса Foo. Без неполных типов это приведет к возникновению ошибки компилятора, т.к. классы существуют в одном и том же пространстве имен. С ключевым словом partial мы может отметить для компилятора, что этот класс, возможно, имеет другие описания где-то в другом месте.

    File1.cs File2.cs
    public partial class Foo
    {
    public void MyFunction()
    {
    // do something here
    }
    
    public partial class Foo
    {
       public void MyOtherFunction()
       {
          // do something here
       }
    }
    

    Таблица 2.

    Во время компилирования компилятор C# собирает все описания неполного типа и комбинирует их. Результирующий IL, сгенерированный компилятором, показывает единый комбинированный класс, а не несколько составляющих класса.

    Стандартизация

    В декабре 2001 язык программирования C# был утвержден Европейской Ассоциацией производителей компьютеров (European Computer Manufacturer's Association (ECMA)) как стандартный (ECMA 334). Вскоре после этого стандарт C# был отправлен на рассмотрение в Международную организацию по стандартизации (ISO), где он также вскоре будет стандартизован (уже стандартизован – прим shatokhin). Важной вехой в развитии нового языка программирования является создание стандарта C#, что несет за собой перспективу множества реализаций на различных платформах операционных систем. В самом деле, некоторое количество сторонних производителей компиляторов и исследователей реализовали стандарт и создали собственные версии компилятора C#.

    Доступность

    Обсуждаемые здесь возможности будут доступны в следующей версии C# компилятора. В начале 2003 версия Visual Studio .NET "Everett" будет содержать немного модифицированную для полного соответствия требованиям ECMA версию C# . эта версия не будет включать возможности, обсуждаемые в этой статье. Компания Microsoft надеется, что эти возможности будут включены в версию "VS for Yukon" Visual Studio, дата выхода которой еще не определена.

    В течение следующих нескольких месяцев Microsoft будет публиковать информацию по этим возможностям, включая полные спецификации. Приглашаем сообщество программистов и разработчиков языков программирования высказать свое мнение по этим и другим языковым возможностям, которые покажутся вам интересными. Вы можете обратиться к разработчикам языка программирования C# по адресу: sharp@microsoft.com (mailto:sharp@microsoft.com).

    Дополнительная информация

    Web сайт C# сообщества: http://www.csharp.net (http://www.csharp.net)

    Web сайт Visual C#™ Product: http://msdn.microsoft.com/vcsharp (http://msdn.microsoft.com/vcsharp)

    Никакая часть настоящей статьи не может быть воспроизведена или передана в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические, если на то нет письменного разрешения владельцев авторских прав.

    Материал, изложенный в данной статье, многократно проверен. Но, поскольку вероятность технических ошибок все равно существует, сообщество не может гарантировать абсолютную точность и правильность приводимых сведений. В связи с этим сообщество не несет ответственности за возможные ошибки, связанные с использованием статьи.
     
         

       
       
         
      VBNet рекомендует