0x55——C#中的Lambda Expression

本文主要参考MSDN上的Lambda Expression(C# Programming Guide),博文Lambda Expression和code project上的博文Exploring Lambda Expression in C#

Introduction

Lambda表达式是一个匿名函数,含有表达式(Expression)以及语句(statements),可以用于构造delegates和expression tree。

所有的Lambda Expression都有一个Lambda Expression符号=>,念作“goes to”,符号的左侧是函数参数,右侧则是表达式和语句,“x => x * x”就念作“x goes to x times x”,并且可以赋值给一个delegates。就如同如下:

del MyDelegate = x => x * x;
Console.WriteLine("MyDelegate(5) = {0}", MyDelegate(5));

 

或者构建一个Expression Tree Type:

Expression<del> myET = x => x * x;
Console.WriteLine("MyET(5) = {0}", myET.Compile()(5));

 

=>的运算优先级和赋值运算符(=)相同,且是右连接的。

 

Parameter

一个Lambda表达式具有如下格式:

(input paramter) => expression

如果只有一个参数,可以像Introduction中那样传参。如果有两个或以上,就需要加上括号:

(x, y) => x == y

有时候如果还需要指明类型,可以如下:

(int x, string y) => s.Length == x;

如果不传参数,如下:

() => SomeMethod()

 

Statement Lambdas

Lambdas可以用花括号括起来写很多语句,尽管管理上一般就只写两到三句(更长的就用delegate一类的吧)

delegate void TestDelegate(string s);

...

// the body of a statement lambda can consist of any number of statements;
// however, in practice there are typically no more than two or three.
TestDelegate myDel = n =>
{
    string s = n + " " + "World";
    Console.WriteLine(s);
};
myDel("Hello");

 

Lambdas With the Standard Query Operators

很多Standard Query Operators都有个输入参数是Func<T1, TResult>,它的定义中,尖括号内,前面的几个都是它的参数arg0, arg1,……,最后一个TResult是它的返回值。譬如:

Func<int, string, bool> myFunc = (x, s) => x == s.Length;
bool result = myFunc(5, "Cuero");
Console.WriteLine("{0}, {1}", result.GetType(), result);

于是下面就是若干个在Standard Query Operators里使用Lambdas的例子:

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int[] numbers2 = { 5,4,9,3};
int odds = numbers.Count(n => n % 2 == 1);
var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);
foreach (int i in firstNumbersLessThan6.ToList()) Console.Write("{0} ", i);
Console.WriteLine();
firstNumbersLessThan6 = numbers2.TakeWhile(n => n < 6);
foreach (int i in firstNumbersLessThan6.ToList()) Console.Write("{0} ", i);
Console.WriteLine();

上面这个例子中,第一次循环到9就停下了,因为此时n == 9, n >= 6了,所以循环停止。

如下是另一个例子:

Func<int, bool> where = n => n < 9;
Func<int, int> select = n => n;
Func<int, string> orderby = n => n % 2 == 0 ? "even" : "odd";
var nums = numbers.Where(where).OrderBy(orderby).Select(select);
foreach (int i in nums.ToList()) Console.Write("{0} ", i);
Console.WriteLine();

OrderBy提供了一个分类的依据,此处应该是按照字母序,如果都是even,可能那就按传入的先后顺序。

 

Variable Scope in Lambda Expressions

Lambdas可以获得其外部的变量。如下是MSDN上的代码:

class Test
{
    D del;
    D2 del2;
    public void TestMethod(int input)
    {
        int j = 0;
        // Initialize the delegates with lambda expressions.
        // Note access to 2 outer variables.
        // del will be invoked within this method.
        del = () => { j = 10;  return j > input; };

        // del2 will be invoked after TestMethod goes out of scope.
        del2 = (x) => {return x == j; };
      
        // Demonstrate value of j:
        // Output: j = 0 
        // The delegate has not been invoked yet.
        Console.WriteLine("j = {0}", j);        // Invoke the delegate.
        bool boolResult = del();

        // Output: j = 10 b = True
        Console.WriteLine("j = {0}. b = {1}", j, boolResult);
    }

    static void Main()
    {
        Test test = new Test();
        test.TestMethod(5);

        // Prove that del2 still has a copy of
        // local variable j from TestMethod.
        bool result = test.del2(10);

        // Output: True
        Console.WriteLine(result);
           
        Console.ReadKey();
    }
}

不过此时应该注意如下几点:

  1. 使用的变量不会被垃圾收集器收走,直到匿名函数作用结束。
  2. Lambdas内部的变量在外部是不可见的。
  3. Lambdas不能直接获得ref和out参数(必须传进去)。
  4. Lambdas里面的return语句不会使整个大函数return。
  5. 不能用goto、break和continue语句跳到Lambdas之外的标签。

关于其上的第三点,在这里给一个例子:

delegate void OutParameter(out int i);
delegate void RefParameter(ref int i);
public static void GotchasWithLambdas()
{
    // must explicitly specify the parameter type

    // example with out parameter int i;
    int i;
    OutParameter something = (out int x) => x = 5;
    something(out i);
    Console.WriteLine("out i = {0}", i);

    // example with ref parameter int i;
    int a = 2;
    RefParameter test = (ref int x) => x++;
    test(ref a);
    Console.WriteLine("ref a = {0}", a);
}

 

The Action Delegates Type

另外,Lambdas还能和Action配合使用,简要代码如下:

class Program
{
    public static void SomeAsynchronousMethod(Action<string> complete, string s)
    {
        Thread.Sleep(1000);
        complete(s);
    } 
    static void Main(string[] args)
    {
        SomeAsynchronousMethod(((string s) => { Console.WriteLine(s + " done"); }), "Cuero");
    }
}

关于这部分内容,以及Expression Tree,在本文都不作太多的介绍,只是贴一些简单的例子而已。

posted @ 2012-04-24 10:25  cuero  阅读(587)  评论(0编辑  收藏  举报