C # 2.0 specification (2)

zhaozj2021-02-16  55

(Connected)

This article is a Microsoft's technical article for translation. For reference for learning C #, please do not use for commercial purposes. http://msdn.microsoft.com/vcsharp/team/language/default.aspx

19.1.5 generic method

In some cases, the type parameters are not required throughout the class, but only within a particular method. Often, this is the case when creating a generic type as a parameter. For example, when using the STACK class that earlier described, a universal pattern may be pressed into multiple values ​​in a row, and it is also very convenient to write a method in a single call. For specific constructive types, such as Stack , this method looks like this.

Void Pushmultiple (stack stack, params int [] value)

{

Foreach (int value in values)

Stack.push (value);

}

This method can be used to press the plurality of int values ​​to a stack .

Stack stack = new stack ();

Pushmultiple (stack, 1, 2, 3, 4);

However, the previous method is only effective for specific structural types Stack . To make it work for Stack , the method must be written as a generic method. The generic method specifies one or more type parameters between the "<" and ">" dividers after the name of the method. Type parameters can be used within the parameter list, return type, and method body. A generic PushmultiPle method will be like this.

Void Pushmultiple (Stack <> t stack, params t [] value

{

FOREACH (T Value In Values) stack.push (value);

}

With this generic method, you can press multiple items to any Stack . When a generic method is called, the type parameter value is given in the polar brackets of the method call. E.g

Stack stack = new stack ();

Pushmultiple (stack, 1, 2, 3, 4);

This generic PushMultiPle method is more reused than the previous version because it can work on any Stack , but it seems that it is not convenient when the call is called because T is required to be passed to the method as a type of parameters. In many cases, the compiler uses a Type Inferencing process to deliver the correct type parameters from other parameters passed to the method. In the previous example, because the first official parameter is the stack type, the subsequent parameters are int types, so the compiler can inform the type parameter value must be INT. Thus, the type parameters may not be specified when the generic PushMultiPle method is called.

Stack stack = new stack ();

Pushmultiple (stack, 1, 2, 3, 4);

19.2 anonymous method

Event handle and other callback functions often need to be called through special delegation, never call directly. Nonetheless, we can only put the code of the event handle and the callback function, in a specific method, and explicitly create a delegate to this method. Instead, anonymous method allows a delegated code to be written by the inline to use, which is convenient to make the code directly for the instance of the entrusted. In addition to this convenience, the anonymous method also shared access to the function members included in the local statement. To make your name method reach a sharing (distinguish between anonymous methods), you need to manually create an auxiliary class, and local members will increase (lifting "for the class. The following example shows a simple input form that contains a list box, a text box, and a button. When the button is pressed, an item containing text in the text box is added to the list box.

Class InputForm: Form

{

Listbox listbox;

TextBox textbox;

Button addbutton;

Pubic myform ()

{

Listbox = new listbox (...);

Textbox = new textbox (...);

AddButon = New Button (...);

AddButton.Click = New EventHandler (AddClick);

}

Void AddClick (Object Sender, Eventargs E)

{

Listbox.tems.add (TextBox.text);

}

}

Even a unique statement is required even if the response to the Click event of the button needs to be executed. That statement must also be placed in a separate method with a full parameter list, and must also manually create an EventHandler delegation that references that method. Using an anonymous method, the event processing code will become quite concise.

Class InputForm: Form

{

Listbox listbox;

TextBox textbox;

Button addbutton;

Pubic myform ()

{

Listbox = new listbox (...);

Textbox = new textbox (...);

AddButon = New Button (...);

AddButton.click = delegate {

Listbox.tems.add (TextBox.Text.);

}

}

The anonymous method consists of keyword delegate and an optional parameter list, and a statement in the "{" and "}" delimiter. The anonymous method in the previous example did not use the parameters provided by the commission, so the parameter list is omitted. If you want to access the parameters, the anonymous method can contain a list of parameters.

AddButton.Click = Delegate (Object sender, Eventargs E) {

Messagebox.show ((Button Sender) .text);

}

In the previous example, an implicit conversion will occur once from anonymous methods to the EventHandler delegate type (type of Click event). This implicit conversion is possible because the return value of the parameter list and the type of delegate is compatible with the anonymous method. The exact rules about compatibility are as follows:

If one of the following is true, the delegated parameter list is compatible with anonymous methods.

- Anonymous method does not have a list of parameters, and delegate no OUT parameters.

- An anonymous method contains a precise match with the parameters of the delegate, the number, type, and modifier are precisely matched.

If one of the following is true, then the return type of the delegate is compatible with the anonymous method.

- The return type of the commission is Void, the anonymous method does not return the statement, or there is only a return statement without an expression. - The return type of the commission is not Void, and in an anonymous method, all RETURN statements related expressions can be implicitly converted to the type of delegate.

Before the implicit conversion of the delegate type, the delegated parameter list and the return type must be compatible with the anonymous method.

The following example writes the "inline" function using an anonymous method. The anonymous method is passed as a function of the function.

Using system;

DELEGATE DOUBLE FUNCTION (Double X);

Class test

{

Static Double [] Apply (double [] a, function f)

{

Double [] result = new double [a.length];

For (int i = 0; i

Return Result;

}

Static Double [] MultiplyAllby (double [] a, double factor)

{

Return Apply (A, DELEGATE (Double X) {Return X * Factor;})

}

Static void

Main

()

{

Double [] a = {0.0, 0.5, 1.0};

Double [] Squares = Apply (A, DELEGATE (Double X) {Return x * x});

Double [] Doubles = MultiplyAllby (a, 2.0);

}

}

Apply method Applicable to a given function for a Double [] element, and returns a double [] as a result. In the main method, the second parameter passed to Apply is an anonymous method that is compatible with the FUCNtion commission. The anonymous method is just a square of the parameter, and the result of the Apply call is a double [], the square of the value is included in A.

MultiplyLBY method returns a Double [] created by a given Factor by a given factor (factor). To get the result, multiplyallby calls the Apply method and passes an anonymous method (by multiplying factor factor).

If a local variable or parameter scope includes an anonymous method, the variable and parameters are referred to as an external variable of an anonymous method (Outer Variable). In the multiplyAllby method, A and Factors are an external variable that passes to the anonymous method of Apply, because the anonymous method references Factor, Factor being captured by the anonymous method (Capture). Typically, the living period of the local variable is limited to the execution area of ​​the block or statement it associated. However, the captured external variable will always survive to the anonymous method referenced by the commission can be received by garbage.

19.2.1 Method Group Conversion

As described above, the anonymous method can be implicitly converted to a compatible entrustment type. For a method group, C # 2.0 allows this same type of conversion, that is, without explicitly instantiate the delegation in almost any circumstances. For example, the following statement

AddButton.Click = New EventHandler (AddClick);

Apply (A, New Function (Math.sin));

Can be replaced by the following statement.

AddButton.click = addClick;

Apply (a, math.sin);

When using this short form, the compiler will automatically infer which delegate type needs to be instantiated, but its final effect is the same as longer expression. 19.3 iterator

The ForeACH statement of the C # is used to iterate all the elements of an enumerable (ENUMERABLE) collection. In order to be enumerated, the collection must have a non-parameter GetENUMERATOR method, which returns an ENUMERTOR (enumerator). Under normal circumstances, the enumerator is difficult to implement, but this problem uses an iterator that is greatly simplified.

The iterator is a statement block that generates an ordered sequence. The iterator is different from a conventional language block with one or more Yield statements.

The Yield Return statement produces the next value of iteration.

Yield BREAK statement indicates that iteration has been completed.

As long as the return type of the function member is one of the enumerat interface, an iterator can be used as a function body, or an enumerable interface.

The enumerator interface is system.collections.ienumerator and the type constructed by sysetm.collections.generic.ienumerator .

Enumerable interfaces are system.collections.ienumerable and types constructed by system.collections.Generic.ienumerable .

Iterative is not a member, it is just a way to implement a function member, it is important to understand this. A member that is implemented by an iterator can be overwritten and overloaded by other may or impossible to be implemented by an iterator.

The following Stack class uses the iterator to implement its GetENUMERATOR method. This iterator sequentially enumerates all the elements from the top to the end.

Using system.collections.Generic;

Public Class Stack : ienumerable

{

T [] items;

INT country;

Public void push (t data) {...}

Public T Pop () {...}

Public IEnumerator getenumerator ()

{

For (INT i = count-1; i> = 0; - i) {

Yield Return Items [i];

}

}

}

The presence of the GetEnumerator method makes Stack become an enumerable type that makes the instance of Stack can be used in the foreach statement. The following example is pressed into a value from 0 to 9 to an integer stack, and uses a Foreach loop to display all values ​​from the top to the bottom of the stack.

Using system;

Class test

{

Static void

Main

()

{

Stack stack = new stack ();

For (int i = 0; i <10; i ) stack.push (i);

Foreach (INT I in Stack) Console.Write ("{0}", i);

Console.writeLine ();

}

}

Example of the output is entered:

9 8 7 6 5 4 3 2 1 0

The Foreach statement implicitly invokes the collection of non-parameter GeTenumerator methods to get an enumerator. Only one such non-parameter GetENUMERATOR method is defined by the collection, but there is often a variety of enumeration methods, as well as methods of controlling enumeration through parameters. In this case, the collection can use the iterator to return the properties and methods of returning one of the enumerable interfaces. For example, Stack may introduce two new attributes of IENUMERABLE type, TopTobottom, and Bottomtotop. Using system.collections.Generic;

Public Class Stack : ienumerable

{

T [] items;

INT country;

Public void push (t data) {...}

PUBLIC T POP () P {...}

Public IEnumerator getenumerator ()

{

For (INT i = count-1; i> = 0; - i)

{

Yield Return Items [i];

}

}

Public IENUMERABLE Topbottom {

Get {

Return this;

}

}

Public IEnumerable bottomtotop {

Get {

For (int i = 0; i

{

Yield Return Items [i];

}

}

}

}

The GET Accessor of the TopTobottom property is just returning this because the stack itself is enumerable. The Bottomtotop property returns an enumeration implementation using the C # iterator. The following example shows how attributes are used to enumerate stack elements.

Using system;

Class test

{

Static void

Main

()

{

Stack stack = new stack ();

For (int i = 0; i <10; i ) stack.push (i);

For (INT I in Stack..toptobottom) console.write ("{0}", i);

Console.writeLine ();

For (INT I in stack..bottotop) console.write ("{0}", i);

Console.writeLine ();

}

}

Of course, these attributes can also be used outside of the foreach statement. The following example passes the result of the call attribute to a separate Print method. This example also shows an iterator for a method body for FROMTOBY accepting parameters.

Using system;

Using system.collections.Generic;

Class test

{

Static void Print (IEnumerable collection)

{

Foreach (Int I in Collection) Console.Write ("{0}", i);

Console.writeLine ();

}

Static IEnumerable frOMTOBY (int from, int)

{

For (int I = from; i <= to; i = by)

{

Yield Return I;

}

}

Static void

Main

()

{

Stack stack = new stack ();

For (int i = 0; i <10; i ) stack.push (i); print (stack.toptobottom);

Print (stack.bottomtotop);

Print (FromToby (10, 20, 2));

}

}

The output of this example is as follows.

9 8 7 6 5 4 3 2 1 0

0 1 2 3 4 5 6 7 8 9

10 12 14 16 18 20

Pan and non-float enumeration interfaces contain a single member, a GetEnumerator method that does not accept parameters, which is an enumerator interface. A enumeration acts as an enumerator factory. Whenever a GeTenumerator method that correctly implements an enumerated interface, a separate enumerator is generated. It is assumed that the internal state of the enumeration has not changed between the GetEnumerator twice, and the returned enumerator should generate an enumeration value of the same order. In the example below, this should be maintained, even if the enumeration is overlapped.

Using system;

Using system.collections.Generic;

Class test

{

Static IEnumerable FromTo (int from, int to)

{

While (from <= to) Yield Return from ;

}

Static void

Main

()

{

IEnumerable E = fromto (1, 10);

Foreach (INT X in E)

{

Foreach (Int Y in E)

{

Console.writeline ("{0,3}", x * y);

}

Console.writeLine ();

}

}

}

The previous code prints a multiplication table of integers 1 to 10. Note that the FROMTO method is only called for an enumeration E. However, E.GETENUMERATOR () is called multiple times (via foreach statement) to generate multiple equivalents. These enumerators have encapsulated iterator code specified in the FROMTO statement. Note that iterator code modifies the FROM parameter.

However, the enumerator is operated independently because each enumerator gives from and TO its own copy. The sharing of transitional status between enumeors is one of many subtle defects, and should be avoided when enumerating and enumerators. The design of the C # iterator can be used to avoid these problems, thereby achieving robust enumerations and enumerators in a simple and intuitive place.

转载请注明原文地址:https://www.9cbs.com/read-25907.html

New Post(0)