C # 2.0 specification (ii)

Source: Internet
Author: User
Tags sin

19.1.5 generic methods

In some cases, the type parameter is not required for the entire class, but only within a specific method. Often, this is the case when creating a method that accepts a generic type as a parameter. For example, when using the Stack<t> class described earlier,

A common pattern might be to push multiple values in a row, and it is convenient to write a method in a single call. For a particular constructed type, such as Stack<int>, this method looks like this.

void Pushmultiple (stack<int> stack, params int[] values) {foreach (int value in values) Stack. Push (value);} This method can be used to push multiple int values into a stack<int>. stack<int> stack = new stack<int> (); Pushmultiple (Stack, 1, 2, 3, 4);

However, the previous method is only valid for a particular constructed type stack<int>. To make it work for stack<t>, the method must be written as a generic method. The generic method is followed by the name of the method between the "<" and the ">" delimiter

One or more type parameters are specified. Type parameters can be used within the parameter list, the return type, and the method body. A generic Pushmultiple method will be the case.

void Pushmultiple<t> (stack<>t stack, params t[] values) {foreach (T value in values) Stack. Push (value);}

With this generic method, you can press multiple entries into any stack<t>. When a generic method is called, the type parameter value is given in the angle brackets of the method call. For example

stack<int> stack = new stack<int> (); Pushmultiple<int> (Stack, 1,2,3,4);

This generic Pushmultiple method is more reusable than the previous version, because it can work on any stack<t>, but it seems inconvenient at the time of invocation, because T is required to be passed as a type parameter to the method. In many cases, the compiler uses a process called type inferencing, which infers the correct type parameters from other parameters passed to the method. In the previous example, because the first formal parameter is the stack<int> type, and subsequent arguments are of type int, the compiler can infer that the type parameter value must be int. As a result, you can call a generic Pushmultiple method without specifying a type parameter.

stack<int> stack = new stack<int> (); Pushmultiple (Stack, 1, 2, 3, 4);

19.2 Anonymous Methods

Event handlers and other callback functions often need to be called through a dedicated delegate and are never called directly. Even so, we can only put the event handle and callback function code in a particular method, and then explicitly create a delegate for this method. Conversely, anonymous methods (anonymous method) allow the code associated with a delegate to be written inline with the local law of the delegate, and it is convenient that this makes the code straightforward for the instance of the delegate. In addition to this convenience, anonymous methods share access to function members that are contained in local statements. In order for a named method to be shared (as distinct from anonymous methods), it is necessary to create the auxiliary class manually and the local member "promote (lifting)" As the domain of 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 in the text box that contains text 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.Items.Add (textbox. Text);} }

Even if the response to the button's Click event is only a single statement that needs to be executed. The statement must also be placed in a separate method with a complete argument list, and the EventHandler delegate that references that method must also be created manually. With anonymous methods, the event-handling code becomes 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.items.add (TextBox.Text.);}

The anonymous method consists of a keyword delegate and an optional argument list, as well as a statement enclosed in the "{" and "}" delimiters. In the previous example, the anonymous method did not use the arguments supplied by the delegate, so the argument list was omitted. If you want to access a parameter, an 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 from an anonymous method to a EventHandler delegate type (the type of the Click event) occurs. This implicit conversion is possible because the return value of the parameter list and delegate type is compatible with the anonymous method. The exact rules for compatibility are as follows:

If one of the following is true, then the argument list of the delegate is compatible with the anonymous method.

-The anonymous method has no argument list, and the delegate does not have an out parameter.
-The anonymous method contains a parameter list that matches the parameters of the delegate exactly on the quantity, type, and modifier.

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 delegate is void, the anonymous method does not return a statement, or only a return statement without an expression.
-The return type of the delegate is not void, and in an anonymous method, all return statement-related expressions can be implicitly converted to the type of the delegate.

Before an implicit conversion of a delegate type occurs, both the argument list and the return type of the delegate must be compatible with the anonymous method.

The following example writes an "inline" function using an anonymous method. An anonymous method is passed as a function delegate type.

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<a.length;i++) result[i] = f (a[i]); return r Esult;} 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};d ouble[] squares = Apply (A, delegate (double x) {return x*x});d ouble[] Doubles = MultiplyAllBy (A, 2.0);}} 

The Apply method applies the given function of a double[] element and returns a double[] as the result. In the main method, the second argument passed to apply is an anonymous method that is compatible with the Fucntion delegate type. The anonymous method simply returns the square of the parameter, and the result of the apply call is a double[], which contains the square of the value in a.

The MultiplyAllBy method returns a double[] created by multiplying each value in the parameter array A by a given factor (factor). To get the result, MultiplyAllBy invokes the Apply method and passes it an anonymous method (with a factor of factor in the parameters).

If the scope of a local variable or parameter includes an anonymous method, the variable and argument are called an external variable (outer variable) of the anonymous method. In the MultiplyAllBy method, a and factor are external variables passed to the anonymous method of apply, because the anonymous method references the Factor,factor captured by an anonymous method (capture) [/b]. Typically, the lifetime of a local variable is limited to the execution area of the block or statement it is associated with. However, the captured external variables will persist until the anonymous method referenced by the delegate can be garbage collected.

19.2.1 Method Group Conversions

As previously described, an anonymous method can be implicitly converted to a compatible delegate type. For a group of methods, c#2.0 allows this same type of conversion, that is, in almost any case, you do not need to instantiate the delegate explicitly. 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 this short form is used, the compiler automatically infers which delegate type needs to be instantiated, but its final effect is the same as the longer expression.

19.3 iterators

The

C # foreach statement is used to iterate over all elements of an enumerable (enumerable) collection. In order to be enumerated, the collection must have a parameterless GetEnumerator method that returns a Enumertor (enumerator). In general, enumerators are difficult to implement, but this problem is greatly simplified by using iterators. A

Iterator is a statement block that produces an ordered sequence of values. Iterators differ from the regular block of statements that have one or more yield statements. The

yield return statement produces the next value of the iteration. The
yield break statement indicates that the iteration has completed.

An iterator can be used as a function body as long as the return type of a function member is one of the enumerator interfaces (enumerator interface) or an enumerable interface (enumerable interface). The

Enumerator interface is System.Collections.IEnumerator and the type constructed by sysetm.collections.generic.ienumerator<t>. The
enumerable interfaces are System.Collections.IEnumerable and types constructed by system.collections.generic.ienumerable<t>. The


iterator is not a member, it is just a way to implement a function member, and it is important to understand this. A member that is implemented through an iterator can be overridden and overloaded by other members that might or may not be implemented through iterators. The Stack<t> class under

uses iterators to implement its GetEnumerator method. This iterator sequentially enumerates all the elements of the stack from the top to the bottom.

Using System.collections.generic;public class stack<t>:ienumerable<t>{t[] items;int count;public void Push (T data) {...} Public T Pop () {...} Public ienumerator<t> GetEnumerator () {for (int i =count-1;i>=0;--i) {yield return items[i];}}}

The existence of the GetEnumerator method makes stack<t> an enumerable type, which enables instances of stack<t> to be used in a foreach statement. The following example presses the value from 0 to 9 into an integer stack, and uses a Foreach loop to sequentially display all values from the top of the stack.

Using System;class test{static void Main () {stack<int> Stack = new stack<int> (); for (int i=0;i<10;i++) Stack. Push (i); foreach (int i in stack) Console.Write ("{0}", i); Console.WriteLine ();}}

The output of the example is entered below:

9 8 7 6 5 4 3 2 1 0

The foreach statement implicitly invokes the parameterless GetEnumerator method of a collection to obtain an enumerator. There can be only one such parameterless GetEnumerator method defined by the collection, but there are often multiple enumerations and methods for controlling enumerations through parameters. In this case, the collection can use iterators to implement properties and methods that return one of the enumerable interfaces. For example,stack<t> may introduce new properties of two ienumerable<t> types, TopToBottom and bottomtotop.

Using System.collections.generic;public class Stack<t>: ienumerable<t>{t[] items;int count;public void Push (T data) {...} Public T Pop () p{...} Public ienumerator<t> GetEnumerator () {for (int i= count-1;i>=0;--i) {yield return items[i];}} Public ienumerable<t> Topbottom{get{return this;}} Public ienumerable<t> bottomtotop{get{for (int I = 0;i<count;i++) {yield return items[i];}}}

The get accessor of the TopToBottom property simply returns this because the stack itself is enumerable. The BottomToTop property returns an enumeration implemented using the C # iterator. The following example shows how a property is used to enumerate stack elements.

Using System;class test{static void Main () {stack<int> Stack = new stack<int> (); for (int i = 0; i<10; i++) St Ack. Push (i); for (int i in stack: TopToBottom) Console.Write ("{0}", i); Console.WriteLine (); for (int i in stack: BottomToTop) Console.Write ("{0}", i); Console.WriteLine ();} }

Of course, these properties can also be used outside of the foreach statement. The following example passes the result of the invocation property to a separate print method. This example also shows an iterator that is used as a method body for Fromtoby accept parameters.

Using system;using system.collections.generic;class test{static void Print (Ienumerable<int> collection) { foreach (int i in collection) Console.Write ("{0}", i); Console.WriteLine ();} static ienumerable<int> Fromtoby (int from, int. to, Int. by) {for (int i =from; i<=to; I +=by) {yield return i;}} static void Main () {stack<int> stack = new stack<int> (); 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 00 1 2 3 4 5 6 7 8 910 12 14 16 18 20

Generic and non-generic enumerable interfaces contain a single member, an GetEnumerator method that does not accept parameters, and an enumerator interface. An enumeration acts as an enumerator factory. Whenever a GetEnumerator method of a class that correctly implements an enumerable interface is called, a separate enumerator is generated. Assuming that the internal state of the enumeration has not changed between two calls to GetEnumerator, the enumerator returned should produce the same enumeration value in the same order as the collection. In the example below, this should be maintained even if the lifetime of the enumeration overlaps.

Using system;using system.collections.generic;class test{static ienumerable<int> FromTo (int from, int. to) {while ( FROM<=TO) yield return from++;} static void Main () {ienumerable<int> 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 printed a multiplication table of integers 1 through 10. Note that the FromTo method is only called once to generate the enumeration E. However, E.getenumerator () is called several times (through a foreach statement) to produce multiple equivalent enumerators. These enumerators encapsulate the iterator code specified in the FromTo declaration. Note The iterator code modifies the from parameter.

However, enumerators work independently because each enumerator gives its own copy of From and to. The sharing of transitions between enumerators is one of many subtle flaws that should be avoided when implementing enumerations and enumerators. C # iterators are designed to avoid these problems, enabling robust enumerations and enumerators in a straightforward and intuitive way.

The above is C # 2.0 specification (ii) content, more relevant content please pay attention to topic.alibabacloud.com (www.php.cn)!


  • Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.