Recipe9.12.Using Anonymous Methods


Recipe 9.12. Using Anonymous Methods

Problem

There is a new feature in C# 2.0 called anonymous methods. While anonymous methods can be viewed as syntactic sugar for making delegate calls less difficult, you want to understand all of the different ways that they can be used to help you in your daily programming chores as well as understand the ramifications of those uses.

Solution

Anonymous methods can be implemented and used in a variety of ways:

  • Using return values and parameters

  • Written inline or through delegate inference (explained shortly)

  • Using generic type parameters

Let's start with the original way to use delegates. First you would declare a delegate type, DoWork in this case, and then you would create an instance of it (as shown here in the WorkItOut method). Declaring the instance of the delegate requires that you specify a method to execute when the delegate is invoked, and here the DoWorkMethodImpl method has been connected. The delegate is fired and the text is written to the console via the DoWorkMethodImpl method.

 // Declare delegate. class OldWay {     // Declare delegate.     delegate int DoWork(string work);          // Have a method to create an instance of and call the delegate.     public void WorkItOut()     {         // Create instance.         DoWork dw = new DoWork(DoWorkMethodImpl);         // Invoke delegate         int i = dw("DoWorkMethodImpl1");     }     // Have a method that the delegate is tied to with a matching signature     // so that it is invoked when the delegate is called.     public int DoWorkMethodImpl(string s)     {         Console.WriteLine(s);         return s.GetHashCode();     } } 

Anonymous methods allow you to set up code to run when a delegate is invoked but there does not need to be a formal method declaration that is given to the delegate. For example, you could have written the preceding code using an anonymous method like this:

 class NewWay {     // Declare delegate.     delegate int DoWork(string work);     // Have a method to create an instance of and call the delegate.     public void WorkItOut()     {         // Declare instance.         DoWork dw = delegate(string s)         {             Console.WriteLine(s);             return s.GetHashCode();         };         // Invoke delegate.         int i = dw("DoWorkMethodImpl1");     } } 

Notice that instead of having a method called DoWorkMethodImpl, you use the delegate keyword to directly assign the code from that method inline to the DoWork delegate. The assignment looks like this:

 DoWork dw = delegate(string s) {     Console.WriteLine(s);     return s.GetHashCode(); }; 

You also provide the parameter required by the DoWork delegate (string) and your code returns an int (s.GetHashCode()) as the delegate requires. When setting up an anonymous method, the code must match the delegate signature or you will get a compiler error.

There is yet another way you can set up the delegate using anonymous methods and that is through the magic of delegate inference. Delegate inference allows you to assign the method name directly to the delegate instance without having to write the code for creating a new delegate object. Under the covers, C# actually writes the IL for creating the delegate object, but you don't have to do it explicitly here. Using delegate inference instead of writing out new delegate() everywhere helps to unclutter the code involved in the usage of delegates, as shown here:

 class DirectAssignmentWay {     // Declare delegate.     delegate int DoWork(string work);     // Have a method to create an instance of and call the delegate.     public void WorkItOut()     {         // Declare instance and assign method.         DoWork dw = DoWorkMethodImpl;         // invoke delegate         int i = dw("DoWorkMethodImpl1");     }     // Have a method that the delegate is tied to with a matching signature     // so that it is invoked when the delegate is called.     public int DoWorkMethodImpl(string s)     {         Console.WriteLine(s);         return s.GetHashCode();     } } 

Notice that all that is assigned to the DoWork delegate instance dw is the method name DoWorkMethodImpl. There is no "new DoWork(DoWorkMethodImpl)" call as there was in older C# code.

Remember, the underlying delegate wrapper does not go away, delegate inference just simplifies the syntax a bit by hiding some of it.


Alternatively, you can also set up anonymous methods that take generic type parameters to enable working with generic delegates as you do here in the GenericWay class:

 class GenericWay {     // Declare generic delegate.     delegate T DoGenericWork<T>(T t);     // Have a method to create two instances of and call the delegates.     public void WorkItOut()     {         DoGenericWork<string> dwString = delegate(string s)         {             Console.WriteLine(s);             return s;         };                  // Invoke string delegate.         string retStr = dwString("DoWorkMethodImpl1");         DoGenericWork<int> dwInt = delegate(int i)         {             Console.WriteLine(i);             return i;         };                  // Invoke int delegate.         int j = dwInt(5);     } } 

The DoGenericWork delegate is defined with one type parameter, T, which is used to specify the type of the returned value as well as the single parameter passed. Setting up the delegate this way allows the WorkItOut method to create two instances of DoGenericWork, one using string and the other using int as the type.

Discussion

One of the most useful things about anonymous methods is the concept of outer variables. The official definition of outer variables is that they are any local variable, value parameter, or parameter array with a scope that contains the anonymous method.

What does this mean? It means that, inside of the code of the anonymous method, you can touch variables outside of the scope of that method. There is a concept of "capturing" the variables that occurs when an anonymous method actually makes reference to one of the outer variables. In the following example, the count variable is captured and incremented by the anonymous method. The count variable is not part of the original scope of the anonymous method but part of the outer scope. It is incremented and then the incremented value is returned and totaled.

 delegate int Count(); int count = 0; int total = 0; Count countUp = delegate { return count++; }; for(int i=0;i<10;i++) {     total += countUp(); } Debug.WriteLine("Total = " + total); 

What capturing actually does is extend the lifetime of the outer variable to coincide with the lifetime of the underlying delegate instance that represents the anonymous method. This should encourage you to be careful about what you touch from inside an anonymous method. You could be causing things to hang around a lot longer than you originally planned on. The garbage collector won't get a chance to clean up those outer variables until later once they are used in the anonymous method. Capturing outer variables has another garbage-collector effect: when locals or value parameters are captured, they are no longer considered to be fixed but are now movable, so any unsafe code must now fix that variable before use by using the fixed keyword.

Outer variables can affect how the compiler generates the internal IL for the anonymous method. If the anonymous method uses outer variables, it is generated as a nested class, rather than as another private method of the class it is declared in, as it otherwise would be. If the outer method is a static, then the anonymous method can access only static variables, as the nested class will also be generated as a static.

A few last things to remember about anonymous methods:

  • They can't use break, goto, or continue to jump from the anonymous method to a target outside the anonymous method block.

  • No unsafe code can be executed inside of an anonymous method.

See Also

See the "Anonymous Methods" topic in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net