Generic and Anonymous Methods in C#

 

Generic methods:

In some cases a type parameter is not needed for an entire class, but only inside a particular method. Often, this occurs when creating a method that takes 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 might be convenient to write a method that does so in a single call. For a particular constructed type, such as stack<int>, the method would look 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 onto a stack<int>: 

 

Stack<int>stack=new stack<int>();

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

 

However, the method above only works with the particular constructed type stack<int>. To have it work with any stack<T>, the method must be written as a generic method. A generic method has one or more type parameters specified in <and> delimiters after the method name. The type parameters can be used within the parameter list, return type, and body of the method. A generic pushMultiple method would look like this.

 

Void pushMultiple<T>(stack<T>stack, params T[] values){ 

Foreach(T value in values) stack.push(value); 

}

 

Using this generic method;it is possible to push multiple items onto any stack<T>.When calling a genrric method, type arguments are given in angle brackets in the method invocation. 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, since it works on any stack<T>, but it appears to be less convenient to call, since the desired T must be supplied as a type argument to the method. In many cases, however, the compiler can deduce the correct type argument from the other argument passed to the method, using a process called type inferencing. In the example above, since the first regular argument is of type stack<int>, and the subsequent arguments are of type int, the compiler can reason that the type parameter must be int. Thus, the generic pushMultiple method can be called without specifying the type parameter;

 

Stack<int>stack=new stack<int>(); 

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

 

Anonymous methods:

Event handlers and other callbacks are often invoked exclusively through delegates and never directly. Even so, it has thus far been necessary to place the code of event handlers and callbacks in distinct methods to which delegates are explicitly created. In contrast, anonymous methods allow the code associated with a delegate to be written “in-line” where the delegate is used, conveniently trying the code directly to the delegate instance. Besides this convenience, anonymous methods have shared access to the local state of the containing function member. To achieve the same state sharing using named methods requires “lifting” local variables into fields in instances of manually authored helper classes.

 

The following example shows a simple input form that contains a list box, a text box, and a button. When the button is clicked, an item containing the text in the text box is added to the list box.

 

Class InputFrom:From 

{ 

 ListBox listBox; 

Textbox textbox; 

Button addButton;

 

Public MyFrom(){ 

   listBox = new listbox(…); 

   textBox =new TextBox(…); 

   add Button =new Button(…); 

   addButton.Click + =new EventHandler(AddClick); 

}

 

Void addclick(object sender, EventArgs e){ 

listBox.Items.Add(textbox.Text); 

} 

}

 

Even though only a single statement is executed in response to the button’s click event, that statement must be extracted into a separate method with a full parameter list, and an EventHandler delegate referencing that method must  be manually created. Using an anonymous method, the event handling code becomes significantly more succinct:

 

Class InputForm:Form  

{ 

   ListBox listBox; 

   TextBox textbox;

   Button addButton; 

Public MyFrom(){ 

listBox =new Listbox(…); 

textbox=new TextBox(…); 

addButton=new Button(…); 

addButton.click+=delegate{ 

listBox.Items.Add(textbox.Text); 

}; 

} 

}

 

An anonymous method consists of the keyword delegate, an optional parameter list, and a statement list enclosed in { and } delimiters. The anonymous method in the previous example does’nt use the parameters supplied by the delegate, and it can therefore omit the parameter list. To gain access to the parameters, the anonymous method can include a parameter list:

 

addButton.Click += delegate(object sender, EventArgs e) 

{ 

MessageBox.Show((Button)sender). Text); 

};

 

In the previous examples, an implicit conversion occurs from the anonymous method to the EventHandler delegate type (the type of the Click event). This implicit conversion is possible the parameter list and return type of the delegate type are compatible with the anonymous method. The exact rules for compatibility are as follows:

 

*The parameter list of a delegate with an anonymous method if one of the following is true:

 ->The anonymous method has no parameter list and the delegate has no out parameters.

 ->The anonymous method includes a parameter list that exactly matches the delegate’s parameters in number, types, and modifiers.

 *The return type of a delegate is compatible with an anonymous method if one of the following is true:

 ->The delegate’s return type is void and the anonymous method has no return statements or only return statements with no expression.

 ->The delegate’s return type is not void and the expressions with all return statements in the anonymous method can be implicitly converted to the return type of the delegate.

 Both the parameter list and the return type of a delegate must be compatible with an anonymous method before an implicit conversion to that delegate type can occur.

 The following example uses anonymous methods to write functions “in-line.” The anonymous methods are passed as parameters of 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 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); 

} 

}

 

The Apply method applies a given Function to the element of a double [], returning a double [] with the results. In the Main method, the second parameter passed to Apply is an anonymous method that is compatible with the Function delegate type. The anonymous method simply returns the square of its argument, and thus the result of that Apply invocation is a double[] containing the squares of the values in a.

 

The MultiplyAllBy method returns a double [] created by multiplying each of the values in the argument array a by a given factor. In order to produce its result, MultiplyAllBy invokes the Apply method, passing an anonymous method that multiplies the argument x by factor.

Local variables and parameters whose scope contains an anonymous method are called outer variables of the anonymous method. In the MultiplyAllBy method, a and factor are outer variables of the anonymous method passed to Apply, and because the anonymous method references factor, factor is said to have been captured by the anonymous method. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated. However, the lifetime of a captured outer variable is extended at least until the delegate referring to the anonymous method becomes eligible for garbage collection.