Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Instance variable initializersСодержание книги
Поиск на нашем сайте
When an instance constructor has no constructor initializer, or it has a constructor initializer of the form base(...), that constructor implicitly performs the initializations specified by the variable-initializers of the instance fields declared in its class. This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor and before the implicit invocation of the direct base class constructor. The variable initializers are executed in the textual order in which they appear in the class declaration. Constructor execution Variable initializers are transformed into assignment statements, and these assignment statements are executed before the invocation of the base class instance constructor. This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to that instance are executed. Given the example using System; class A public virtual void PrintFields() {} } class B: A public B() { public override void PrintFields() { when new B() is used to create an instance of B, the following output is produced: x = 1, y = 0 The value of x is 1 because the variable initializer is executed before the base class instance constructor is invoked. However, the value of y is 0 (the default value of an int) because the assignment to y is not executed until after the base class constructor returns. It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the constructor-body. The example using System; class A public A() { public A(int n) { class B: A public B(): this(100) { public B(int n): base(n – 1) { contains several variable initializers; it also contains constructor initializers of both forms (base and this). The example corresponds to the code shown below, where each comment indicates an automatically inserted statement (the syntax used for the automatically inserted constructor invocations isn’t valid, but merely serves to illustrate the mechanism). using System.Collections; class A public A() { public A(int n) { class B: A public B(): this(100) { public B(int n): base(n – 1) { Default constructors If a class contains no instance constructor declarations, a default instance constructor is automatically provided. That default constructor simply invokes the parameterless constructor of the direct base class. If the class is abstract then the declared accessibility for the default constructor is protected. Otherwise, the declared accessibility for the default constructor is public. Thus, the default constructor is always of the form protected C(): base() {} or public C(): base() {} where C is the name of the class. If overload resolution is unable to determine a unique best candidate for the base class constructor initializer then a compile-time error occurs. In the example class Message a default constructor is provided because the class contains no instance constructor declarations. Thus, the example is precisely equivalent to class Message public Message(): base() {} Private constructors When a class T declares only private instance constructors, it is not possible for classes outside the program text of T to derive from T or to directly create instances of T. Thus, if a class contains only static members and isn’t intended to be instantiated, adding an empty private instance constructor will prevent instantiation. For example: public class Trig public const double PI = 3.14159265358979323846; public static double Sin(double x) {...} The Trig class groups related methods and constants, but is not intended to be instantiated. Therefore it declares a single empty private instance constructor. At least one instance constructor must be declared to suppress the automatic generation of a default constructor. Optional instance constructor parameters The this(...) form of constructor initializer is commonly used in conjunction with overloading to implement optional instance constructor parameters. In the example class Text public Text(int x, int y): this(x, y, null) {} public Text(int x, int y, string s) { the first two instance constructors merely provide the default values for the missing arguments. Both use a this(...) constructor initializer to invoke the third instance constructor, which actually does the work of initializing the new instance. The effect is that of optional constructor parameters: Text t1 = new Text(); // Same as Text(0, 0, null) Static constructors A static constructor is a member that implements the actions required to initialize a closed class type. Static constructors are declared using static-constructor-declarations: static-constructor-declaration: static-constructor-modifiers: static-constructor-body: A static-constructor-declaration may include a set of attributes (§17) and an extern modifier (§10.6.7). The identifier of a static-constructor-declaration must name the class in which the static constructor is declared. If any other name is specified, a compile-time error occurs. When a static constructor declaration includes an extern modifier, the static constructor is said to be an external static constructor. Because an external static constructor declaration provides no actual implementation, its static-constructor-body consists of a semicolon. For all other static constructor declarations, the static-constructor-body consists of a block which specifies the statements to execute in order to initialize the class. This corresponds exactly to the method-body of a static method with a void return type (§10.6.10). Static constructors are not inherited, and cannot be called directly. The static constructor for a closed class type executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain: · An instance of the class type is created. · Any of the static members of the class type are referenced. If a class contains the Main method (§3.1) in which execution begins, the static constructor for that class executes before the Main method is called. To initialize a new closed class type, first a new set of static fields (§10.5.1) for that particular closed type is created. Each of the static fields is initialized to its default value (§5.2). Next, the static field initializers (§10.4.5.1) are executed for those static fields. Finally, the static constructor is executed.
The example using System; class Test class A class B must produce the output: Init A because the execution of A’s static constructor is triggered by the call to A.F, and the execution of B’s static constructor is triggered by the call to B.F. It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state. The example using System; class A static A() { class B static B() {} static void Main() { produces the output X = 1, Y = 2 To execute the Main method, the system first runs the initializer for B.Y, prior to class B’s static constructor. Y’s initializer causes A’s static constructor to be run because the value of A.X is referenced. The static constructor of A in turn proceeds to compute the value of X, and in doing so fetches the default value of Y, which is zero. A.X is thus initialized to 1. The process of running A’s static field initializers and static constructor then completes, returning to the calculation of the initial value of Y, the result of which becomes 2. Because the static constructor is executed exactly once for each closed constructed class type, it is a convenient place to enforce run-time checks on the type parameter that cannot be checked at compile-time via constraints (§10.1.5). For example, the following type uses a static constructor to enforce that the type argument is an enum: class Gen<T> where T: struct Destructors A destructor is a member that implements the actions required to destruct an instance of a class. A destructor is declared using a destructor-declaration: destructor-declaration: destructor-body: A destructor-declaration may include a set of attributes (§17). The identifier of a destructor-declarator must name the class in which the destructor is declared. If any other name is specified, a compile-time error occurs. When a destructor declaration includes an extern modifier, the destructor is said to be an external destructor. Because an external destructor declaration provides no actual implementation, its destructor-body consists of a semicolon. For all other destructors, the destructor-body consists of a block which specifies the statements to execute in order to destruct an instance of the class. A destructor-body corresponds exactly to the method-body of an instance method with a void return type (§10.6.10). Destructors are not inherited. Thus, a class has no destructors other than the one which may be declared in that class. Since a destructor is required to have no parameters, it cannot be overloaded, so a class can have, at most, one destructor. Destructors are invoked automatically, and cannot be invoked explicitly. An instance becomes eligible for destruction when it is no longer possible for any code to use that instance. Execution of the destructor for the instance may occur at any time after the instance becomes eligible for destruction. When an instance is destructed, the destructors in that instance’s inheritance chain are called, in order, from most derived to least derived. A destructor may be executed on any thread. For further discussion of the rules that govern when and how a destructor is executed, see §3.9. The output of the example using System; class A class B: A class Test is B’s destructor since destructors in an inheritance chain are called in order, from most derived to least derived. Destructors are implemented by overriding the virtual method Finalize on System.Object. C# programs are not permitted to override this method or call it (or overrides of it) directly. For instance, the program class A public void F() { contains two errors. The compiler behaves as if this method, and overrides of it, do not exist at all. Thus, this program: class A is valid, and the method shown hides System.Object’s Finalize method. For a discussion of the behavior when an exception is thrown from a destructor, see §16.3. Iterators A function member (§7.5) implemented using an iterator block (§8.2) is called an iterator. An iterator block may be used as the body of a function member as long as the return type of the corresponding function member is one of the enumerator interfaces (§10.14.1) or one of the enumerable interfaces (§10.14.2). It can occur as a method-body, operator-body or accessor-body, whereas events, instance constructors, static constructors and destructors cannot be implemented as iterators. When a function member is implemented using an iterator block, it is a compile-time error for the formal parameter list of the function member to specify any ref or out parameters. Enumerator interfaces The enumerator interfaces are the non-generic interface System.Collections.IEnumerator and all instantiations of the generic interface System.Collections.Generic.IEnumerator<T>. For the sake of brevity, in this chapter these interfaces are referenced as IEnumerator and IEnumerator<T>, respectively. Enumerable interfaces The enumerable interfaces are the non-generic interface System.Collections.IEnumerable and all instantiations of the generic interface System.Collections.Generic.IEnumerable<T>. For the sake of brevity, in this chapter these interfaces are referenced as IEnumerable and IEnumerable<T>, respectively. Yield type An iterator produces a sequence of values, all of the same type. This type is called the yield type of the iterator. · The yield type of an iterator that returns IEnumerator or IEnumerable is object. · The yield type of an iterator that returns IEnumerator<T> or IEnumerable<T> is T. Enumerator objects When a function member returning an enumerator interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. Instead, an enumerator object is created and returned. This object encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object’s MoveNext method is invoked. An enumerator object has the following characteristics: · It implements IEnumerator and IEnumerator<T>, where T is the yield type of the iterator. · It implements System.IDisposable. · It is initialized with a copy of the argument values (if any) and instance value passed to the function member. · It has four potential states, before, running, suspended, and after, and is initially in the before state. An enumerator object is typically an instance of a compiler-generated enumerator class that encapsulates the code in the iterator block and implements the enumerator interfaces, but other methods of implementation are possible. If an enumerator class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use (§2.4.2). An enumerator object may implement more interfaces than those specified above. The following sections describe the exact behavior of the MoveNext, Current, and Dispose members of the IEnumerable and IEnumerable<T> interface implementations provided by an enumerator object. Note that enumerator objects do not support the IEnumerator.Reset method. Invoking this method causes a System.NotSupportedException to be thrown. The MoveNext method The MoveNext method of an enumerator object encapsulates the code of an iterator block. Invoking the MoveNext method executes code in the iterator block and sets the Current property of the enumerator object as appropriate. The precise action performed by MoveNext depends on the state of the enumerator object when MoveNext is invoked: · If the state of the enumerator object is before, invoking MoveNext: o Changes the state to running. o Initializes the parameters (including this) of the iterator block to the argument values and instance value saved when the enumerator object was initialized. o Executes the iterator block from the beginning until execution is interrupted (as described below). · If the state of the enumerator object is running, the result of invoking MoveNext is unspecified. · If the state of the enumerator object is suspended, invoking MoveNext: o Changes the state to running. o Restores the values of all local variables and parameters (including this) to the values saved when execution of the iterator block was last suspended. Note that the contents of any objects referenced by these variables may have changed since the previous call to MoveNext. o Resumes execution of the iterator block immediately following the yield return statement that caused the suspension of execution and continues until execution is interrupted (as described below). · If the state of the enumerator object is after, invoking MoveNext returns false. When MoveNext executes the iterator block, execution can be interrupted in four ways: By a yield return statement, by a yield break statement, by encountering the end of the iterator block, and by an exception being thrown and propagated out of the iterator block. · When a yield return statement is encountered (§8.14): 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. · When a yield break statement is encountered (§8.14): 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. · 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. · 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. The Current property An enumerator object’s Current property is affected by yield return statements in the iterator block. When an enumerator object is in the suspended state, the value of Current is the value set by the previous call to MoveNext. When an enumerator object is in the before, running, or after states, the result of accessing Current is unspecified. For an iterator with a yield type other than object, the result of accessing Current through the enumerator object’s IEnumerable implementation corresponds to accessing Current through the enumerator object’s IEnumerator<T> implementation and casting the result to object. The Dispose method The Dispose method is used to clean up the iteration by bringing the enumerator object to the after state. · If the state of the enumerator object is before, invoking Dispose changes the state to after. · If the state of the enumerator object is running, the result of invoking Dispose is unspecified. · 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. · If the state of the enumerator object is after, invoking Dispose has no affect. Enumerable objects When a function member returning an enumerable interface type is implemented using an iterator block, invoking the function member does not immediately execute the code in the iterator block. Instead, an enumerable object is created and returned. The enumerable object’s GetEnumerator method returns an enumerator object that encapsulates the code specified in the iterator block, and execution of the code in the iterator block occurs when the enumerator object’s MoveNext method is invoked. An enumerable object has the following characteristics: · It implements IEnumerable and IEnumerable<T>, where T is the yield type of the iterator. · It is initialized with a copy of the argument values (if any) and instance value passed to the function member. An enumerable object is typically an instance of a compiler-generated enumerable class that encapsulates the code in the iterator block and implements the enumerable interfaces, but other methods of implementation are possible. If an enumerable class is generated by the compiler, that class will be nested, directly or indirectly, in the class containing the function member, it will have private accessibility, and it will have a name reserved for compiler use (§2.4.2). An enumerable object may implement more interfaces than those specified above. In particular, an enumerable object may also implement IEnumerator and IEnumerator<T>, enabling it to serve as both an enumerable and an enumerator. In that type of implementation, the first time an enumerable object’s GetEnumerator method is invoked, the enumerable object itself is returned. Subsequent invocations of the enumerable object’s GetEnumerator, if any, return a copy of the enumerable object. Thus, each returned enumerator has its own state and changes in one enumerator will not affect another. The GetEnumerator method An enumerable object provides an implementation of the GetEnumerator methods of the IEnumerable and IEnumerable<T> interfaces. The two GetEnumerator methods share a common implementation that acquires and returns an available enumerator object. The enumerator object is initialized with the argument values and instance value saved when the enumerable object was initialized, but otherwise the enumerator object functions as described in §10.14.4. Implementation example 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() { The GetEnumerator method can be translated into an instantiation of a compiler-generated enumerator class that encapsulates the code in the iterator block, as shown in the following. class Stack<T>: IEnumerable<T> public IEnumerator<T> GetEnumerator() { class __Enumerator1: IEnumerator<T>, IEnumerator public __Enumerator1(Stack<T> __this) { public T Current { object IEnumerator.Current { public bool MoveNext() { public void Dispose() { void IEnumerator.Reset() { In the preceding translation, the code in the iterator block is turned into a state machine and placed in the MoveNext method of the enumerator class. Furthermore, the local variable i is turned into a field in the enumerator object so it can continue to exist across invocations of MoveNext. The following example prints a simple multiplication table of the integers 1 through 10. The FromTo method in the example returns an enumerable object and is implemented using an iterator. using System; class Test static void Main() { The FromTo method can be translated into an instantiation of a compiler-generated enumerable class that encapsulates the code in the iterator block, as shown in the following. using System; class Test static IEnumerable<int> FromTo(int from, int to) { class __Enumerable1: public __Enumerable1(int __from, int to) { public IEnumerator<int> GetEnumerator() { IEnumerator IEnumerable.GetEnumerator() { public int Current { object IEnumerator.Current { public bool MoveNext() { public void Dispose() { void IEnumerator.Reset() { The enumerable class implements both the enumerable interfaces and the enumerator interfaces, enabling it to serve as both an enumerable and an enumerator. The first time the GetEnumerator method is invoked, the enumerable object itself is returned. Subsequent invocations of the enumerable object’s GetEnumerator, if any, return a copy of the enumerable object. Thus, each returned enumerator has its own state and changes in one enumerator will not affect another. The Interlocked.CompareExchange method is used to ensure thread-safe operation. The from and to parameters are turned into fields in the enumerable class. Because from is modified in the iterator block, an additional __from field is introduced to hold the initial value given to from in each enumerator. The MoveNext method throws an InvalidOperationException if it is called when __state is 0. This protects against use of the enumerable object as an enumerator object without first calling GetEnumerator. The following example shows a simple tree class. The Tree<T> class implements its GetEnumerator method using an iterator. The iterator enumerates the elements of the tree in infix order. using System; class Tree<T>: IEnumerable<T> public Tree(T value, Tree<T> left, Tree<T> right) { public IEnumerator<T> GetEnumerator() { class Program static Tree<T> MakeTree<T>(params T[] items) { // The output of the program is: static void Main() { Tree<string> strings = MakeTree( The GetEnumerator method can be translated into an instantiation of a compiler-generated enumerator class that encapsulates the code in the iterator block, as shown in the following. class Tree<T>: IEnumerable<T> public IEnumerator<T> GetEnumerator() { class __Enumerator1: IEnumerator<T>, IEnumerator public __Enumerator1(Node<T> __this) { public T Current { object IEnumerator.Current { public bool MoveNext() { case 0: case 1: __left_dispose: __yield_value: case 2: case 3: __right_dispose: __end: } public void Dispose() { case 1: case 3: } void IEnumerator.Reset() { The compiler generated temporaries used in the foreach statements are lifted into the __left and __right fields of the enumerator object. The __state field of the enumerator object is carefully updated so that the correct Dispose() method will be called correctly if an exception is thrown. Note that it is not possible to write the translated code with simple foreach statements. Async Functions A method (§10.6) or anonymous function (§7.15) with the async modifier is called an async function. In general, the term async is used to describe any kind of function that has the async modifier. It is a compile-time error for the formal parameter list of an async function to specify any ref or out parameters. The return-type of an async method must be either void or a task type. The task types are System.Threading.Tasks.Task and types constructed from System.Threading.Tasks.Task<T>. For the sake of brevity, in this chapter these types are referenced as Task and Task<T>, respectively. An async method returning a task type is said to be task-returning. The exact definition of the task types is implementation defined, but from the language’s point of view a task type is in one of the states incomplete, succeeded or faulted. A faulted task records a pertinent exception. A succeeded Task< T > records a result of type T. Task types are awaitable, and can therefore be the operands of await expressions (§7.7.7). An async function invocation has the ability to suspend evaluation by means of await expressions (§7.7.7) in its body. Evaluation may later be resumed at the point of the suspending await expression by means of a resumption delegate. The resumption delegate is of type System.Action, and when it is invoked, evaluation of the async function invocation will resume from the await expression where it left off. The current caller of an async function invocation is the original caller if the function invocation has never been suspended, or the most recent caller of the resumption delegate otherwise.
|
||||
Последнее изменение этой страницы: 2016-08-10; просмотров: 284; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.137.163.206 (0.01 с.) |