|
|||||||
АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция |
И напоследок... блок finallyПоследним моментом, о котором нужно обязательно сказать при рассмотрении итераторов в C#, являются проблемы, связанные с блоком finally внутри блока итераторов. Давайте в последний пример добавим блок try/finally и, даже не глядя на сгенерированный код, подумаем о его поведении и возможных последствиях:
Как уже было сказано ранее, блок итератора не выполняется последовательно, а разворачивается в конечный автомат, реализация которого находится в методе MoveNext. Очевидно, блок finally должен выполняться не после каждого вызова метода MoveNext, а только один раз на полную итерацию последовательности, поскольку в противном случае мы можем, например, освободить ресурсы, которые потребуются при следующей итерации цикла. А раз так, то кто сможет гарантировать, что пользователь захочет пройти последовательность целиком? Почему, получив итератор из метода GetNumbers, пользователь обязательно должен вызвать MoveNext более одного раза? Но, несмотря на то, что компилятор не может гарантировать вызов блока finally итератора, он делает все возможное, чтобы свести такую вероятность к минимуму. Давайте рассмотрим сгенерированный код:
Если блок итератора содержит блок finally, то весь код, расположенный в этом блоке, помещается в отдельный метод (в нашем случае в метод m_Finally3()), который будет вызван в следующих случаях: 1. После нормального завершения итерирования коллекции (либо после вызова yield break, либо после обыкновенного завершения блока итератора); 2. В случае генерации исключения в блоке итератора (вы могли обратить внимание на ключевое слово fault вместо finally; это не ошибка, такого ключевого слова нет в языке C#, но в языке IL такая конструкция существует, и означает, что этот фрагмент кода будет выполнен только в случае генерации исключения, после чего исключение будет проброшено далее по стеку); 3. В случае вызова метода Dispose. На последнем случае давайте остановимся подробнее и попытаемся понять, для чего вообще итератор реализует интерфейс IDisposable. Итак, что произойдет, если исключение произойдет не в коде итератора, а в коде, его использующем, при обработке первого элемента коллекции? Давайте снова вернемся к последнему примеру:
Разработчик вправе предполагать, что если между строками 1 и 3 (т.е. при обработке первого элемента коллекции пользовательским кодом) произойдет исключение, то блок finally должен быть выполнен точно так же, как и при возникновении исключения непосредственно в блоке итератора, например, в строке 1. Такое поведение становится более очевидным, если вместо возвращения двух магических чисел блок итератора будет выполнять более осмысленную работу, например, открывать файл и возвращать строки по одной:
В этом фрагменте блок try/finally генерирует за нас компилятор, но это никак не влияет на поведение. Давайте еще раз зададимся вопросом: вправе ли разработчик рассчитывать на корректное освобождение ресурсов, если пользовательский код сгенерирует исключение? К сожалению, ответ на этот вопрос будет утвердительным в том случае, если пользователь будет следовать общепринятым идиомам использования итераторов: воспользуется оператором foreach, либо реализует аналогичную функциональность самостоятельно:
Компилятор преобразовывает оператор foreach следующим образом:
В таком случае, если при обработке элемента коллекции возникнет исключение, то автоматически будет вызван метод Dispose итератора, что приведет к вызову блока finally блока итератора. When a yield return statement is encountered: o The expression given in the statement is evaluated, implicitly converted to the yield type, and assigned to the Current property of the enumerator object. o Execution of the iterator body is suspended. The values of all local variables and parameters (including this) are saved, as is the location of this yield return statement. If the yield return statement is within one or more try blocks, the associated finally blocks are not executed at this time. o The state of the enumerator object is changed to suspended. o The MoveNext method returns true to its caller, indicating that the iteration successfully advanced to the next value. 6. When a yield break statement is encountered: o If the yield break statement is within one or more try blocks, the associated finally blocks are executed. o The state of the enumerator object is changed to after. o The MoveNext method returns false to its caller, indicating that the iteration is complete. 7. When the end of the iterator body is encountered: o The state of the enumerator object is changed to after. o The MoveNext method returns false to its caller, indicating that the iteration is complete. 8. When an exception is thrown and propagated out of the iterator block: o Appropriate finally blocks in the iterator body will have been executed by the exception propagation. o The state of the enumerator object is changed to after. o The exception propagation continues to the caller of the MoveNext method. o The Dispose method is used to clean up the iteration by bringing the enumerator object to the after state. 9. If the state of the enumerator object is before, invoking Dispose changes the state to after. 10. If the state of the enumerator object is running, the result of invoking Dispose is unspecified. 11. If the state of the enumerator object is suspended, invoking Dispose: o Changes the state to running. o Executes any finally blocks as if the last executed yield return statement were a yield break statement. If this causes an exception to be thrown and propagated out of the iterator body, the state of the enumerator object is set to after and the exception is propagated to the caller of the Dispose method. o Changes the state to after. 12. If the state of the enumerator object is after, invoking Dispose has no affect. This section describes a possible implementation of iterators in terms of standard C# constructs. The implementation described here is based on the same principles used by the Microsoft C# compiler, but it is by no means a mandated implementation or the only one possible. The following Stack<T> class implements its GetEnumerator method using an iterator. The iterator enumerates the elements of the stack in top to bottom order. using System; class Stack<T>: IEnumerable<T> public void Push(T item) { public T Pop() { public IEnumerator<T> GetEnumerator() {
using System; class Test { static void Main() {
Понятие атрибутов в языке C#. Создание пользовательских атрибутов. Анализ атрибутов во время выполнения программы. Понятие рефлексии (reflection) в языке C#. Сериализация объектов.
Понятие атрибутов в языке C#
Types, members, and other entities in a C# program support modifiers that control certain aspects of their behavior. For example, the accessibility of a method is controlled using the public, protected, internal, and private modifiers. C# generalizes this capability such that user-defined types of declarative information can be attached to program entities and retrieved at run-time. Programs specify this additional declarative information by defining and using attributes. The following example declares a HelpAttribute attribute that can be placed on program entities to provide links to their associated documentation.
using System; public class HelpAttribute: Attribute public HelpAttribute(string url) { public string Url { public string Topic { All attribute classes derive from the System.Attribute base class provided by the.NET Framework. Attributes can be applied by giving their name, along with any arguments, inside square brackets just before the associated declaration. If an attribute’s name ends in Attribute, that part of the name can be omitted when the attribute is referenced. For example, the HelpAttribute attribute can be used as follows.
[Help("http://msdn.microsoft.com/.../MyClass.htm")] This example attaches a HelpAttribute to the Widget class and another HelpAttribute to the Display method in the class. The public constructors of an attribute class control the information that must be provided when the attribute is attached to a program entity. Additional information can be provided by referencing public read-write properties of the attribute class (such as the reference to the Topic property previously). Поиск по сайту: |
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Студалл.Орг (0.009 сек.) |