Click here to Skip to main content
15,867,963 members
Articles / Programming Languages / C#
Article

C#2 Anonymous Methods

Rate me:
Please Sign up or sign in to vote.
4.78/5 (33 votes)
15 Jun 200613 min read 112.2K   397   96   13
This article presents a new feature named anonymous methods added to the 2.0 version of the C# language. Unlike generics, this feature does not involve new IL instructions. All the magic happens at the level of the compiler.

Image 1

TitlePractical .NET2 and C#2
AuthorPatrick Smacchia
PublisherParadoxalPress
PublishedJanuary 2006
ISBN0-9766132-2-0
PriceUSD 59.95 (ParadoxalPress price: USD 33.99)
Pages896
Websitewww.PracticalDOT.NET (Browse and download the 647 listings and sample chapters)

The following article is excerpted from the chapter 14 of the book Practical .NET2 and C#2.

Contents

This article presents a new feature named anonymous methods added to the 2.0 version of the C# language. Unlike generics, this feature does not involve new IL instructions. All the magic happens at the level of the compiler.

Introduction to C#2 anonymous methods

Let's begin by enhancing some C#1 code to use C#2 anonymous methods. Here is a simple C# v1 program that first references and then invokes a method, through a delegate:

Example1.cs

C#
class Program {
   delegate void DelegateType();
   static DelegateType GetMethod(){
      return new DelegateType(MethodBody);
   }
   static void MethodBody() {
      System.Console.WriteLine("Hello");
   }
   static void Main() {
      DelegateType delegateInstance = GetMethod();
      delegateInstance();
      delegateInstance();
   }
}

Here is the same program rewritten using a C#2 anonymous method:

Example2.cs

C#
class Program {
   delegate void DelegateType();
   static DelegateType GetMethod() {
      return delegate() { System.Console.WriteLine("Hello"); };
   }
   static void Main() {
      DelegateType delegateInstance = GetMethod();
      delegateInstance();
      delegateInstance();
   }
}

You should notice that:

  • The delegate keyword has a new use in C#2. When the C#2 compiler finds the delegate keyword inside the body of a class, it expects it to be followed by an anonymous method body.
  • It is possible to assign an anonymous method to a delegate reference.
  • We understand why this feature is named anonymous method: the method defined in the body of GetMethod() is not named. Nevertheless, it is possible to invoke it because it is referenced by a delegate instance.

You should notice as well that it is possible to use the operator += to allow a delegate instance to reference several methods (anonymous or not):

Example3.cs

C#
using System;
class Program{
   delegate void DelegateType();
   static void Main(){
      DelegateType delegateInstance = delegate() { 
         Console.WriteLine("Hello"); };
      delegateInstance += delegate() { Console.WriteLine("Bonjour"); };
      delegateInstance();
   }
}

As you might expect, this program outputs:

Hello
Bonjour

Anonymous methods can accept arguments

As shown in the following example, an anonymous method can accept arguments of any type. You can also use the keywords ref and out to tune how arguments are passed to the method:

Example4.cs

C#
class Program {
   delegate int DelegateType( int valTypeParam, string refTypeParam, 
                              ref int refParam, out int outParam);
   static DelegateType GetMethod() {
      return delegate( int valTypeParam , string refTypeParam,
                       ref int refParam , out int outParam     ) {
         System.Console.WriteLine( "Hello valParam:{0} refTypeParam:{1}",
                                   valTypeParam, refTypeParam);
         refParam++;
         outParam = 9;
         return valTypeParam;
      }; // End of the body of the anonymous method.
   }
   static void Main() {
      DelegateType delegateInstance = GetMethod();
      int refVar = 5;
      int outVar;
      int i = delegateInstance( 1, "one", ref refVar, out outVar );
      int j = delegateInstance( 2, "two", ref refVar, out outVar );
      System.Console.WriteLine( "i:{0} j:{1} refVar:{2} outVar:{3}", 
                                i, j, refVar, outVar);
   }
}

This program outputs:

Hello valParam:1 refTypeParam:one
Hello valParam:2 refTypeParam:two
i:1 j:2 refVar:7 outVar:9

As you can see, the returned type is not defined inside the anonymous method declaration. The returned type of an anonymous method is inferred by the C# v2 compiler from the returned type of the delegate to which it is assigned. This type is always known because the compiler forces the assignment of any anonymous method to a delegate.

An anonymous method can't be tagged with an attribute. This restriction implies that you can't use the param keyword in the list of arguments of an anonymous method. Indeed, using the keyword param forces the compiler to tag the concerned method with the ParamArray attribute.

Example5.cs

C#
using System;
class Program {
   delegate void DelegateType( params int[] arr );
   static DelegateType GetMethod() {
      // Compilation error: param is not valid in this context.
      return delegate( params int[] arr ){ Console.WriteLine("Hello");};
   }
}

A syntax subtlety

It is possible to declare an anonymous method without any signature, i.e., you are not compelled to write a pair of parenthesis after the keyword delegate if your anonymous method doesn't take any argument. In this case, your method can be assigned to any delegate instance that returns a void type and that doesn't have out arguments. Obviously, such an anonymous method doesn't have access to the parameters that are provided through its delegate invocation.

Example6.cs

C#
using System;
class Program{
   delegate void DelegateType(int valTypeParam, string refTypeParam, 
                              ref int refParam);
   static void Main() {
      DelegateType delegateInstance = delegate { 
         Console.WriteLine( "Hello" ); };
      int refVar = 5;
      delegateInstance( 1, "one", ref refVar );
      delegateInstance( 2, "two", ref refVar );
   }
}

Anonymous methods and generics

As shown in the example below, an argument of an anonymous method can be of a generic type:

Example7.cs

C#
class Foo<T> {
   delegate void DelegateType( T t );
   internal void Fct( T t ) {
      DelegateType delegateInstance = delegate( T arg ){
         System.Console.WriteLine( "Hello arg:{0}" , arg.ToString() ); };
      delegateInstance( t );
   }
}
class Program {
   static void Main() {
      Foo<DOUBLE> inst = new Foo <DOUBLE>();
      inst.Fct(5.5);
   }
}

In .NET 2, a delegate type can be declared with generic arguments. An anonymous method can be assigned to a delegate instance of such a type. You just have to resolve type parameters on both sides of the assignment:

Example8.cs

C#
class Program{
   delegate void DelegateType<T>( T t );
   static void Main() {
      DelegateType<double> delegateInstance = delegate( double arg ) { 
         System.Console.WriteLine( "Hello arg:{0}" , arg.ToString() ); 
      };
      delegateInstance(5.5);
   }
}

Use of anonymous methods in the real world

Anonymous methods are particularly suited to define ‘small’ methods that must be invoked through a delegate. For instance, you might use an anonymous method to code the entry point procedure of a thread:

Example9.cs

C#
using System.Threading;
class Program{
   static void Main(){
      Thread thread = new Thread( delegate() {
        System.Console.WriteLine( "ManagedThreadId:{0} Hello",
                                  Thread.CurrentThread.ManagedThreadId );
      } );
      thread.Start();
      System.Console.WriteLine( "ManagedThreadId:{0} Bonjour",     
                                Thread.CurrentThread.ManagedThreadId );
   }
}

This program displays:

ManagedThreadId:1 Bonjour
ManagedThreadId:3 Hello

Another classic example of this kind of use lies in the Windows Forms control event callbacks:

Example10.cs

C#
public class FooForm : System.Windows.Forms.Form {
   System.Windows.Forms.Button m_Button;
   public FooForm() {
      InitializeComponent();
      m_Button.Click += delegate( object sender, System.EventArgs args ) {
         System.Windows.Forms.MessageBox.Show("m_Button Clicked");
      };
   }
   void InitializeComponent()  {/*...*/}
}

It seems that an anonymous method looks like a tiny language enhancement. It's now time to dig under the hood to realize that anonymous methods are far more complex and can be far more useful.

The C#2 compiler and anonymous methods

The easy way

As you might expect, when an anonymous method is compiled, a new method is created by the compiler in the concerned class:

Example11.cs

C#
class Program {
   delegate void DelegateType();
   static void Main() {
      DelegateType delegateInstance = delegate() { 
         System.Console.WriteLine("Hello"); };
      delegateInstance();
   }
}

The following assembly is the compiled version of the previous program (viewed using the Reflector tool):

Image 2

Indeed, a new private and static method named <Main>b__0() has been automatically generated, and contains the code for our anonymous method. If our anonymous method was declared inside an instance method, the generated method would have been an instance method.

We also note that a delegate field named <>9_CachedAnonymousMethoddelegate1 of type delegateType has been generated to reference our anonymous method.

It is interesting to note that all these generated members can't be viewed with the C# intellisense because their names contain a pair of angle brackets < >. Such names are valid for the IL/CLR syntax but incorrect for the C# syntax.

Captured local variable

To keep things clear and simple, we haven't mentioned yet the fact that an anonymous method can have access to a local variable of its outer method. Let's analyze this feature through the following example:

Example12.cs

C#
class Program {
   delegate int DelegateTypeCounter();
   static DelegateTypeCounter MakeCounter(){
      int counter = 0;
      DelegateTypeCounter delegateInstanceCounter = 
         delegate { return ++counter; };
      return delegateInstanceCounter;
   }
   static void Main() {
      DelegateTypeCounter counter1 = MakeCounter();
      DelegateTypeCounter counter2 = MakeCounter();
      System.Console.WriteLine( counter1() );
      System.Console.WriteLine( counter1() );
      System.Console.WriteLine( counter2() );
      System.Console.WriteLine( counter2() );
   }
}

This program outputs:

1
2
1
2

Think about it, it might stump you. The local variable counter seems to survive when the thread leaves the MakeCounter() method. Moreover, it seems that two instances of this 'surviving' local variable exist!

Note that in .NET 2, the CLR and the IL language haven't been tweaked to support the anonymous method feature. The interesting behavior must stem from the compiler. It's a nice example of syntactic sugar. Let's analyze the assembly:

Image 3

This analysis makes things clear because:

  • The compiler doesn't only create a new method as we saw in the previous section, it utterly creates a new class named <>c__DisplayClass1 in this example.
  • This class has an instance method called <MakeCounter>b__0(). This method has the body of our anonymous method.
  • This class also has an instance field called counter. This field keeps track of the state of the local variable counter. We say the local variable counter has been captured by the anonymous method.
  • The method instantiates the class <>c__DisplayClass1. Moreover, it initializes the field counter of the created instance.

Notice that the MakeCounter() method doesn't have any local variable. For the counter variable, it uses the same field of the generated instance of the class <>c__DisplayClass1.

Before explaining why the compiler has this surprising behavior, let's go further to get a thorough understanding of its work.

Captured local variables and code complexity

The following example is more subtle than expected:

Example13.cs

C#
using System.Threading;
class Program {
   static void Main() {
      for (int i = 0; i < 5; i++)
         ThreadPool.QueueUserWorkItem( delegate { 
            System.Console.WriteLine(i); }, null);
   }
}

This program outputs in a non-deterministic way, something like:

0
1
5
5
5

This result compels us to infer that the local variable i is shared amongst all threads. The execution is non-deterministic because the Main() method and our closure (or anonymous methods) are executed simultaneously by several threads. To make things clear, here is the decompiled code of the Main() method:

private static void Main(){
   bool flag1;
   Program.<>c__DisplayClass1 class1 = new Program.<>c__DisplayClass1();
   class1.i = 0;
   goto Label_0030;
Label_000F:
   ThreadPool.QueueUserWorkItem(new WaitCallback(class1.<Main>b__0), null);
   class1.i++;
Label_0030:
   flag1 = class1.i < 5;
   if ( flag1 ) {
      goto Label_000F;
   }
}

Notice that the fact that the value of 5 being printed indicates that the Main() method is done executing the loop when the display is done. The following version of this program has a deterministic execution:

Example14.cs

C#
using System.Threading;
class Program {
   static void Main() {
      for (int i = 0; i < 5; i++){
         int j = i;
         ThreadPool.QueueUserWorkItem(delegate { 
            System.Console.WriteLine(j); }, null);
      }
   }
}

This time, the program outputs:

0
1
2
3
4

This behavior stems from the fact that the local variable j is captured for each iteration. Here is the decompiled code of the Main() method:

private static void Main(){
   Program.<>c__DisplayClass1 class1;
   bool flag1;
   int num1 = 0;
   goto Label_0029;
Label_0004:
   class1 = new Program.<>c__DisplayClass1();
   class1.j = num1;
   ThreadPool.QueueUserWorkItem(new WaitCallback(class1.<Main>b__0), null);
   num1++;
Label_0029:
   flag1 = num1 < 5;
   if (flag1) {
      goto Label_0004;
   }
}

This sheds light on the fact that capturing local variables with anonymous methods is not an easy thing. You should always take care when using this feature.

Note that a captured local variable is no longer a local variable. If you access such a variable with some unsafe code, you might have pinned it before (with the C# keyword fixed).

An anonymous method accessing an argument of the outer method

Arguments of a method can always be deemed as local variables. Therefore, C#2 allows an anonymous method to use arguments of its outer method. For instance:

Example15.cs

C#
using System;
class Program {
   delegate void DelegateTypeCounter();
   static DelegateTypeCounter MakeCounter( string counterName ) {
      int counter = 0;
      DelegateTypeCounter delegateInstanceCounter = delegate{
         Console.WriteLine( counterName + (++counter).ToString() );
      };
      return delegateInstanceCounter;
   }
   static void Main() {
      DelegateTypeCounter counterA = MakeCounter("Counter A:");
      DelegateTypeCounter counterB = MakeCounter("Counter B:");
      counterA();
      counterA();
      counterB();
      counterB();
   }
}

This program outputs:

Counter A:1
Counter A:2
Counter B:1
Counter B:2

Nevertheless, an anonymous method can't capture an out or ref argument. This restriction is easy to understand as soon as you realize that such an argument can't be seen as a local variable. Indeed, such an argument survives the execution of the method.

An anonymous method accessing a member of the outer class

An anonymous method can access members of its outer class. The case of static member access is easy to understand since there is one and only one occurrence of any static field in the domain application. Thus, there is nothing like ‘capturing' a static field.

The access to the instance of a member is less obvious. To clarify this point, remember that the this reference that allows access to instance members is a local variable of the outer instance method. Therefore, the this reference is captured by the anonymous method. Let's analyze the following example:

Example16.cs

C#
delegate void DelegateTypeCounter();
class CounterBuilder {
   string m_Name; // Un champ d’instance
   internal CounterBuilder( string name ) { m_Name = name; }
   internal DelegateTypeCounter BuildCounter( string counterName ) {
      int counter = 0;
      DelegateTypeCounter delegateInstanceCounter = delegate {
         System.Console.Write( counterName +(++counter).ToString() );
         // On aurait pu écrire this.m_Name.
         System.Console.WriteLine(" Counter built by: " + m_Name); 
      };
      return delegateInstanceCounter;
   }
}
class Program {
   static void Main() {
      CounterBuilder cBuilder1 = new CounterBuilder( "Factory1" );
      CounterBuilder cBuilder2 = new CounterBuilder( "Factory2" );
      DelegateTypeCounter cA = cBuilder1.BuildCounter( "Counter A:" );
      DelegateTypeCounter cB = cBuilder1.BuildCounter( "Counter B:" );
      DelegateTypeCounter cC = cBuilder2.BuildCounter( "Counter C:" );
      cA();  cA ();
      cB();  cB();
      cC();  cC();
   }
}

This program outputs:

Counter A:1 Counter built by: Factory1
Counter A:2 Counter built by: Factory1
Counter B:1 Counter built by: Factory1
Counter B:2 Counter built by: Factory1
Counter C:1 Counter built by: Factory2
Counter C:2 Counter built by: Factory2

Let's decompile the MakeCounter() method to expose the capture of the this reference:

C#
internal DelegateTypeCounter BuildCounter(string counterName){
    CounterBuilder.<>c__DisplayClass1 class1 = new 
                                 CounterBuilder.<>c__DisplayClass1();
    class1.<>4__this = this;
    class1.counterName = counterName;
    class1.counter = 0;
    return new DelegateTypeCounter(class1.<BuildCounter>b__0);
}

Notice that the this reference cannot be captured by an anonymous method that is defined in a structure. Here is the compiler error: Anonymous methods inside structs cannot access instance members of ‘this’. Consider copying ‘this’ to a local variable outside the anonymous method and using the local instead.

Anonymous methods and closures

Definitions: closure and lexical environment

A closure is a function that captures values of its lexical environment, when it is created at run-time. The lexical environment of a function is the set of variables visible from the concerned function.

In previous definitions, we carefully used the terms when and from. It indicates that the notion of closure pinpoints something that exists at run-time (as the concept of object). It also indicates that the notion of lexical environment pinpoints to something that exists in the code, i.e., at compile-time (as the concept of class). Consequently, you can consider that the lexical environment of a C#2 anonymous method is the class generated by the compiler. Following the same idea, you can consider that an instance of such a generated class is a closure.

The definition of a closure also implies the notion of creating a function at run-time. Mainstream imperative languages such as C, C++, C#1, Java, or VB.NET1 don't support the ability to create an instance of a function at run-time. This feature stems from functional languages such as Haskell or Lisp. Thus C#2 goes beyond imperative languages by supporting closures. However, C#2 is not the first imperative language that supports closures, since Perl and Ruby also have this feature.

Ramblings on closures

A function computes its results both from values of its arguments and from the context that surrounds its invocation. You can consider this context as a set of background data. Thus, arguments of a function can be seen as foreground data. Therefore, the decision that an input data of a function must be an argument must be taken from the relevance of the argument for the computation.

Generally, when using object languages, the context of a function (i.e., the context of an instance method) is the state of the object on which it is invoked. When programming with non object oriented imperative languages such as C, the context of a function is the values of the global variables. When dealing with closures, the context is the values of the captured variables when the closure is created. Therefore, as classes, closures are a way to associate behavior and data. In the object oriented world, methods and data are associated, thanks to the this reference. In the functional world, a function is associated with the values of captured variables. To make things clear:

  • You can think of an object as a set of methods attached to a set of data.
  • You can think of a closure as a set of data attached to a function.

Using closures instead of classes

The previous section implies that some type of classes could be replaced by some anonymous methods. Actually, we already perform such replacements in our implementation of the counter. The behavior is the increment of the counter, while the state is its value. However, the counter implementation doesn't harness the possibility of passing arguments to an anonymous method. The following example shows how to harness closures to perform parameterized computation on the state of an object:

Example17.cs

C#
class Program {
   delegate void DelegateMultiplier( ref int integerToMultipl);
   static DelegateMultiplier BuildMultiplier ( int multiplierParam ) {
      return delegate( ref int integerToMultiply ) {
         integerToMultiply *= multiplierParam;
      };
   }
   static void Main() {
      DelegateMultiplier multiplierBy8 = BuildMultiplier(8);
      DelegateMultiplier multiplierBy2 = BuildMultiplier(2);
      int anInteger = 3;
      multiplierBy8( ref anInteger );
      // Here, anInteger is equal to 24.
      multiplierBy2( ref anInteger );
      // Here, anInteger is equal to 48.
   }
}

Here is another example that shows how to harness closures to perform parameterized computation in order to obtain a value from the state of an object:

Example18.cs

C#
using System;
class Article {
   public Article( decimal price ) { m_Price = price; }
   private decimal m_Price;
   public decimal Price { get { return m_Price; } }
}
class Program {
   delegate decimal DelegateTaxComputer( Article article );
   static DelegateTaxComputer BuildTaxComputer( decimal tax ) {
      return delegate( Article article ) {
         return ( article.Price * (100 + tax) ) / 100;
      };
   }
   static void Main(){
      DelegateTaxComputer taxComputer19_6 = BuildTaxComputer(19.6m);
      DelegateTaxComputer taxComputer5_5 = BuildTaxComputer(5.5m);
      Article article = new Article(97);
      Console.WriteLine("Price TAX 19.6% : " + taxComputer19_6(article) );
      Console.WriteLine("Price TAX  5.5% : " + taxComputer5_5(article) );
   }
}

Understand that all the power behind the use of closures in both previous examples comes from the fact that they prevent us from creating small classes (which are in fact created implicitly by the compiler).

Delegates and closures

By taking a closer look, we notice that the notion of a delegate used on an instance method in .NET 1.x is conceptually close to the notion of closure. In fact, such a delegate references both data (the state of the object) and a behavior. A constraint does exist: the behavior must be an instance method of the class defining the type of the this reference.

This constraint is minimized in .NET 2. Because of certain overloads of the Delegate.CreateDelegate() method, you can now reference the first argument of a static method in a delegate. For example:

Example19.cs

C#
class Program {
   delegate void DelegateType( int writeNTime );
   // This method is public to avoid problems of reflection
   // on a non-public member.
   public static void WriteLineNTimes( string s, int nTime ) {
      for( int i=0; i < nTime; i++ )
         System.Console.WriteLine( s );
   }
   static void Main() {
      DelegateType deleg = System.Delegate.CreateDelegate(
         typeof( DelegateType ),
         "Hello",                       
         typeof(Program).GetMethod( "WriteLineNTimes" )) as DelegateType;
      deleg(4);
   }
}

This program displays:

Hello
Hello
Hello
Hello

Note that internally, the implementation of delegates has been completely revised in the 2.0 version of the framework and the CLR. The good news is that the invocation of a method through a delegate is now much more efficient.

Anonymous methods and functors

Introduction to functors

The System namespace contains four new delegate types, particularly useful to manipulate and obtain information from collections:

C#
namespace System {
   public delegate void Action<T> ( T obj ); 
   public delegate bool Predicate<T> ( T obj ); 
   public delegate U    Converter<T,U> ( T from ); 
   public delegate int  Comparison<T> ( T x, T y );
}

The following example exposes four processes which can be done on a list (a request, a calculation, a sort, and a conversion), done using instances of these delegates:

Example20.cs

C#
using System.Collections.Generic;
class Program {
   class Article {
      public Article(decimal price,string name){Price=price;Name=name;}
      public readonly decimal Price;
      public readonly string  Name;
   }

   static bool IsEven(int i) { return i % 2 == 0; }
   static int sum = 0;
   static void AddToSum(int i) { sum += i; }
   static int CompareArticle(Article x, Article y){
      return Comparer<DECIMAL>.Default.Compare(x.Price, y.Price);
   }
   static decimal ConvertArticle(Article article){
      return (decimal)article.Price;
   }

   static void Main(){
      // Seek out every odd integers. 
      // Implicitly uses a ‘Predicate<T>’ delegate object.
      List<INT> integers = new List<INT>();
      for(int i=1; i<=10; i++) { 
         integers.Add(i);
      }
      List<INT> even = integers.FindAll( IsEven );

      // Sum up items of the list.
      // Implicitly uses an ‘Action<T>’ delegate object.
      integers.ForEach( AddToSum );

      // Sort items of type ‘Article’.
      // Implicitly uses a ‘Comparison<T>’ delegate object.
      List<ARTICLE> articles = new List<ARTICLE>();
      articles.Add( new Article(5,"Shoes") );
      articles.Add( new Article(3,"Shirt") );
      articles.Sort( CompareArticle );

      // Cast items of type ‘Article’ into ‘decimal’.
      // Implicitly uses a ‘Converter<T,U>’ delegate object.
      List<DECIMAL> artPrice = articles.ConvertAll<DECIMAL>( ConvertArticle );
   }
}

Readers who have used the Standard Template Library (STL) of C++ will recognize the notion of a functor. A functor is a parameterized process. Functors are particularly useful to complete the same operation on all the elements of a collection. In C++, we overloaded the parenthesis operator in order to implement the notion of a functor. In .NET, a functor takes the form of a delegate instance. In fact, in the previous program, the four delegate instances created implicitly are four examples of functors.

Using anonymous methods and functors to query collections

As shown in the following example, the anonymous methods of C# can prove themselves to be particularly adapted to the implementation of functors. Note that, as with the second functor, which stores the sum of the elements in an integer, a functor can encapsulate a state.

Example21.cs

C#
using System.Collections.Generic;
class Program {
   class Article {
      public Article(decimal price,string name){Price=price;Name=name;}
      public readonly decimal Price;
      public readonly string  Name;
   }
   static void Main(){
      // Seek out every odd integers.
      // Implicitly uses a ‘Predicate<T>’ delegate object.
      List<INT> integers = new List<INT>();
      for(int i=1; i<=10; i++) { 
         integers.Add(i);
      } 
      List<INT> even =integers.FindAll( delegate(int i){ return i%2==0; });

      // Sum up items of the list.
      // Implicitly uses an ‘Action<T>’ delegate object.
      int sum = 0;
      integers.ForEach(delegate(int i) { sum += i; });

      // Sort items of type ‘Article’.
      // Implicitly uses a ‘Comparison<T>’ delegate object.
      List<ARTICLE> articles = new List<ARTICLE>();
      articles.Add( new Article(5,"Shoes") );
      articles.Add( new Article(3,"Shirt") );
      articles.Sort(delegate(Article x, Article y) {
         return Comparer<DECIMAL>.Default.Compare(x.Price,y.Price); } );

      // Cast items of type ‘Article’ into ‘decimal’.
      // Implicitly uses a ‘Converter<T,U>’ delegate object.
      List<DECIMAL> artPrice = articles.ConvertAll<DECIMAL> (
         delegate(Article article) { return (decimal)article.Price; } );
   }
}

List<t> and Array classes support for functors

The use of functors is only possible on collections of type List<T> and Array. In fact, only these collections offer methods which accept functors to process their elements. These methods, which have self-descriptive names, are listed below:

C#
public class List<T> : ... {
   public int FindIndex(Predicate<T> match);
   public int FindIndex(int index, Predicate<T> match);
   public int FindIndex(int index, int count, Predicate<T> match);

   public int FindLastIndex(Predicate<T> match);
   public int FindLastIndex(int index, Predicate<T> match);
   public int FindLastIndex(int index, int count, Predicate<T> match);

   public List<T> FindAll(Predicate<T> match); 
   public T Find(Predicate<T> match);
   public T FindLast(Predicate match);

   public bool Exists(Predicate<T> match);
   public bool TrueForAll(Predicate<T> match); 
 
   public int RemoveAll(Predicate<T> match);
   public void ForEach(Action<T> action); 
   public void Sort(Comparison<T> comparison);
   public List<U> ConvertAll<U>(Converter<T,U> converter);
   ...
}
public class Array {
   public static int FindIndex<T>(T[] array, int startIndex, 
                                  int count, Predicate<T> match);
   public static int FindIndex<T>(T[] array, int startIndex, 
                                  Predicate<T> match);
   public static int FindIndex<T>(T[] array, Predicate<T> match);

   public static int FindLastIndex<T>(T[] array, int startIndex, 
                                      int count, Predicate<T> match);
   public static int FindLastIndex<T>(T[] array, int startIndex, 
                                      Predicate<T> match);
   public static int FindLastIndex<T>(T[] array, Predicate<T> match);

   public static T[] FindAll<T>(T[] array, Predicate<T> match);
   public static T Find<T>(T[] array, Predicate<T> match);
   public static T FindLast<T>(T[] array, Predicate<T> match);

   public static bool Exists<T>(T[] array, Predicate<T> match);
   public static bool TrueForAll<T>(T[] array, Predicate<T> match);

   public static void ForEach<T>(T[] array, Action<T> action);
   public static void Sort<T>(T[] array, System.Comparison<T> comparison);
   public static U[] ConvertAll<T, U>( T[] array, 
                                       Converter<T, U> converter);
   ...
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
France France
Patrick Smacchia is a .NET MVP involved in software development for over 15 years. He is the author of Practical .NET2 and C#2, a .NET book conceived from real world experience with 647 compilable code listings. After graduating in mathematics and computer science, he has worked on software in a variety of fields including stock exchange at Société Générale, airline ticket reservation system at Amadeus as well as a satellite base station at Alcatel. He's currently a software consultant and trainer on .NET technologies as well as the author of the freeware NDepend which provides numerous metrics and caveats on any compiled .NET application.

Comments and Discussions

 
GeneralUse anonymous methods on form controls! Pin
Alphons van der Heijden8-Dec-08 7:05
professionalAlphons van der Heijden8-Dec-08 7:05 
GeneralGreat article and the book receives mostly 5 stars on Amazon Pin
Grav-Vt27-Feb-08 1:43
Grav-Vt27-Feb-08 1:43 
GeneralCollections.RemoveAll() is a perfect example of Anonymous Methods Pin
Sameer Alibhai24-Aug-07 11:12
Sameer Alibhai24-Aug-07 11:12 
GeneralWhy I suggest not to buy this book Pin
Simone Busoli11-Jul-06 17:59
Simone Busoli11-Jul-06 17:59 
QuestionWhat use is this? Pin
craigg7529-Jun-06 5:48
craigg7529-Jun-06 5:48 
AnswerRe: What use is this? Pin
hex2219-Jul-06 21:26
hex2219-Jul-06 21:26 
GeneralVB.Net Pin
Pascal Ganaye26-Jun-06 3:46
Pascal Ganaye26-Jun-06 3:46 
GeneralRe: VB.Net Pin
Colin Angus Mackay26-Jun-06 4:11
Colin Angus Mackay26-Jun-06 4:11 
GeneralNice Pin
Sauron-x20-Jun-06 15:22
Sauron-x20-Jun-06 15:22 
GeneralNow i know where to look Pin
Abi Bellamkonda19-Jun-06 16:06
Abi Bellamkonda19-Jun-06 16:06 
GeneralFunctional C# Programming Pin
Alois Kraus16-Jun-06 23:00
Alois Kraus16-Jun-06 23:00 
GeneralGreat book ! Pin
GriffonRL15-Jun-06 13:19
GriffonRL15-Jun-06 13:19 
GeneralGreat! Pin
Pop Catalin15-Jun-06 11:48
Pop Catalin15-Jun-06 11:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.