November 11, 2008

Comparing Delegates, Anonymous Methods and Lambda Expressions

Here is a good link on this subject. Note the difference between a 'statement lambda' and an 'expression lambda'.
There are now 3 syntaxes for implementing delegates in C#. Here is the same code using the different syntax in each case:
1. Delegate
void StandardDelegateSample()
{
    ISomeResource[] resources =  someFacade.GetResources();
    ISomeResource target = Array.Find(resources, 
       new Predicate(FindTargetSomeResource));
}

// This method implements the test condition for the Find
// method.
private bool FindTargetSomeResource(ISomeResource res)
{
    return (res.Id == TARGET_ID);
}
2. Anonymous Method
void AnonymousMethodSample()
{
    ISomeResource[] resources =  someFacade.GetResources();
    ISomeResource target = Array.Find(resources, 
            delegate(ISomeResource res)
            {
                return (res.InformSourceId == TARGET_ID);
            }
    );
}
3. Lambda Expression
These are an even more shortened version of an anonymous delegate. The argument type can be missed out, the braces, and even the return statement. Like anonymous methods they can support variable capture from the encompassing method, ie. they can refer to the parameters or locals of the hosting method.
void LambdaExpressionSample()
{
  ISomeResource[] resources =  someFacade.GetResources();
  ISomeResource target = Array.Find(resources, 
    res => (res.InformSourceId == TARGET_ID) 
  );
}
Here are the 3 different forms of lambdas:
res => (res.Id == TARGET_ID) // simplest ('Expression Lambda') 
(ISomeResource res) => (res.Id == TARGET_ID) // specify parameter ('Expression Lambda')
(ISomeResource res) => { return (res.Id == TARGET_ID); } // Braces allow for multiple statements ('Statement Lambda')
However there is a gotcha with the use of anonymous methods (and lambda expressions?) so beware.
Code used to validate my lambda expressions:
public class LambdaExpressionTester
{
  public interface ISomeResource
  {
    int Id { get; set; }
              
    // ...
  }

  public class SomeResource : ISomeResource
  {
    public int Id { get; set; }
    //public string Name { get; set; } }
    // ...
  }

  private ISomeResource[] m_SomeResources = 
          new ISomeResource[] {
          new SomeResource{Id=1},
          new SomeResource{Id=4},
          new SomeResource{Id=11},
          new SomeResource{Id=43},
        };

  private static readonly int TARGET_ID = 11;

  public void LambdaExpressionSample()
  {
      ISomeResource[] resources = GetResources();
      ISomeResource target = Array.Find(resources, 
      res => (res.Id == TARGET_ID) 
      //(ISomeResource res) => (res.Id == TARGET_ID)
      //(ISomeResource res) => { return (res.Id == TARGET_ID); }
  );
  }

  private ISomeResource[] GetResources()
  {
      return m_SomeResources;
  }
}

Here is code to demonstrate all the forms of lambda expressions and anonymous delegates:
public void TestAllLambdaForms()
{
    int local = 3;

    // Implicitly typed lambda expression.
    Func<int, int> func1 = x => x+1;
            
    // Lambda expression with statement body.
    Func<int, int> func2 = x => { return x + 1; };

    // Lambda expression using formal parameters with expression body.
    Func<int, int> func3 = (int x) => x + 1;

    // Lambda expression using formal parameters with a statement body.
    Func<int, int> func4 = (int x) => { return x + 1; };

    // Lambda expression using multiple parameters.
    Func<int, int, int> func5 = (x, y) => x * y;

    // Lambda expression using multiple formal parameters.
    Func<int, int, int> func6 = (int x, int y) => x * y;

    // Lambda expression using with no parameters
    Action func7 = () => Console.WriteLine("blah blah blah");

    // Lambda expression accessing a local variable.
    Func<int, int> func8 = x => x * local;

    // Anonymous Method/Delegate with no parameter list.
    Func<int> func10 = delegate { return 1 + 1; };

    // Anonymous Method/Delegate method expression.
    Func<int, int> func11 = delegate(int x) { return x + 1; };

    // Anonymous Method/Delegate expression with 2 parameters.
    Func<int, int, int> func12 = delegate(int x, int y) { return x * y; };

    // Anonymous Method/Delegate accessing a local parameters.
    Func<int, int> func13 = delegate(int x) { return x * local; };

    Console.WriteLine("func1:" + func1.Invoke(1));
    Console.WriteLine("func2:" + func2.Invoke(1));
    Console.WriteLine("func3:" + func3.Invoke(1));
    Console.WriteLine("func4:" + func4.Invoke(1));
    Console.WriteLine("func5:" + func5.Invoke(2, 2));
    Console.WriteLine("func6:" + func6.Invoke(2, 2));
    Console.Write("func7:"); func7.Invoke();
    Console.WriteLine("func8:" + func8.Invoke(1));
    Console.WriteLine("func10:" + func10.Invoke());
    Console.WriteLine("func11:" + func11.Invoke(1));
    Console.WriteLine("func12:" + func12.Invoke(2, 2));
    Console.WriteLine("func13:" + func13.Invoke(1));
}
Here is the output:
func1:2
func2:2
func3:2
func4:2
func5:4
func6:4
func7:blah blah blah
func8:3
func10:2
func11:2
func12:4
func13:3

No comments: