关于c#:. NET Framework中的lambda和委托之间有什么区别?

关于c#:. NET Framework中的lambda和委托之间有什么区别?

What is the difference between lambdas and delegates in the .NET Framework?

我经常被问到这个问题,我想就如何最好地描述两者之间的差异征求一些意见。


它们实际上是两件事。"委托"实际上是持有对方法或lambda的引用的变量的名称,而lambda是没有永久名称的方法。

Lambda与其他方法非常相似,只是有一些细微的区别。

  • 普通方法在"语句"中定义,并与一个永久名称相关联,而lambda在"表达式"中"即时"定义,并且没有永久名称。
  • 某些lambda可以与.NET表达式树一起使用,而方法则不能。
  • 委托的定义如下:

    1
    delegate Int32 BinaryIntOp(Int32 x, Int32 y);

    只要签名是相同的,则BinaryIntOp类型的变量可以分配一个方法或labmda:两个Int32参数和一个Int32返回。

    Lambda可以这样定义:

    1
    BinaryIntOp sumOfSquares = (a, b) => a*a + b*b;

    还要注意的另一件事是,尽管通常将泛型Func和Action类型视为" lambda类型",但它们与任何其他委托一样。关于它们的好处是,它们本质上为您可能需要的任何类型的委托定义了一个名称(最多4个参数,尽管您当然可以添加更多自己的委托)。因此,如果您使用各种各样的委托类型,但不止一次,则可以通过使用Func和Action避免使用委托声明使代码混乱。

    这是Func和Action如何"不仅仅用于lambda"的说明:

    1
    2
    3
    4
    5
    6
    Int32 DiffOfSquares(Int32 x, Int32 y)
    {
      return x*x - y*y;
    }

    Func<Int32, Int32, Int32> funcPtr = DiffOfSquares;

    要知道的另一件事是,具有相同签名但名称不同的委托类型(而不是方法本身)不会被隐式地强制转换为彼此。这包括Func和Action委托。但是,如果签名相同,则可以在它们之间显式转换。

    更加努力。...在C#中,函数很灵活,可以使用lambda和委托。但是C#没有"一流的功能"。您可以使用分配给委托变量的函数名称来创建代表该函数的对象。但这实际上是一个编译器技巧。如果通过在函数名称后加上句点来开始语句(即尝试对函数本身进行成员访问),则会发现那里没有要引用的成员。甚至没有来自对象的。这样可以防止程序员做有用的(当然是潜在的危险)事情,例如添加可以在任何函数上调用的扩展方法。您可以做的最好的事情就是扩展Delegate类本身,这当然也很有用,但还不多。

    更新:另请参阅Karg的答案,该答案说明了匿名委托与方法和lambda之间的区别。

    更新2:James Hart提出了一个重要的建议,尽管非常技术性,但它指出lambda和委托不是.NET实体(即CLR没有委托或lambda的概念),而是它们是框架和语言构造。


    这个问题有点模棱两可,这解释了您得到的答案之间的巨大差异。

    您实际上问过.NET框架中的lambda和委托之间有什么区别;那可能是许多事情之一。您在问:

    • Lambda表达式和C#(或VB.NET)语言中的匿名委托之间有什么区别?

    • .NET 3.5中的System.Linq.Expressions.LambdaExpression对象和System.Delegate对象之间有什么区别?

    • 或介于这些极端之间或周围的某个地方?

    有些人似乎在试图回答" C#Lambda表达式和.NET System.Delegate之间有什么区别?"这个问题,但这没有什么意义。

    .NET框架本身并不理解匿名委托,lambda表达式或闭包的概念,这些都是语言规范定义的。考虑一下C#编译器如何将匿名方法的定义转换为生成的类上带有成员变量的方法,该成员变量具有关闭状态;到.NET,委托没有任何匿名性。对于编写它的C#程序员而言,它只是匿名的。分配给委托类型的lambda表达式也是如此。

    .NET可以理解的是委托的概念-一种描述方法签名的类型,该方法的实例表示对特定对象上的特定方法的绑定调用,或对特定类型上的特定方法的未绑定调用。所述方法遵循所述签名的任何类型的对象。这些类型都继承自System.Delegate。

    .NET 3.5还引入了System.Linq.Expressions命名空间,该命名空间包含用于描述代码表达式的类,因此它还可以表示对特定类型或对象的方法的绑定或未绑定调用。然后,可以将LambdaExpression实例编译为实际的委托(从而对基于表达式结构的动态方法进行代码生成,并返回指向它的委托指针)。

    在C#中,您可以通过为该类型的变量分配一个lambda表达式来生成System.Expressions.Expression类型的实例,这将生成适当的代码以在运行时构造该表达式。

    当然,如果您要问的是Lambda表达式和C#中的匿名方法之间到底有什么区别,那么所有这些几乎都没有关系,在这种情况下,主要的区别是简洁,当您不这样做时,它倾向于匿名委托。不必在意参数,也不要计划返回值,而在要输入推断的参数和返回类型时,请不要使用lambda。

    lambda表达式支持表达式生成。


    一个区别是,匿名委托可以省略参数,而lambda必须与确切的签名匹配。鉴于:

    1
    2
    3
    4
    public delegate string TestDelegate(int i);

    public void Test(TestDelegate d)
    {}

    您可以通过以下四种方式调用它(请注意,第二行包含一个不带任何参数的匿名委托):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Test(delegate(int i) { return String.Empty; });
    Test(delegate { return String.Empty; });
    Test(i => String.Empty);
    Test(D);

    private string D(int i)
    {
        return String.Empty;
    }

    您不能传入不带参数的lambda表达式或不带参数的方法。这些是不允许的:

    1
    2
    3
    4
    5
    6
    7
    Test(() => String.Empty); //Not allowed, lambda must match signature
    Test(D2); //Not allowed, method must match signature

    private string D2()
    {
        return String.Empty;
    }

    委托等效于函数指针/方法指针/回调(请选择),而lambda则是简化了的匿名函数。至少那是我告诉人们的。


    委托基本上基本上只是一个函数指针。 Lambda可以变成委托,但也可以变成LINQ表达式树。例如,

    1
    2
    Func<int, int> f = x => x + 1;
    Expression<Func<int, int>> exprTree = x => x + 1;

    第一行产生一个委托,而第二行产生一个表达式树。


    我对此没有很多经验,但是我要描述的方式是委托是任何函数的包装,而lambda表达式本身就是一个匿名函数。


    很明显,这个问题的原意是" lambda和匿名代表之间有什么区别?"在这里的所有答案中,只有一个人正确无误-主要区别在于可以使用lambda以及表达式树来创建表达式树。

    您可以在MSDN上阅读更多信息:http://msdn.microsoft.com/zh-cn/library/bb397687.aspx


    委托是对具有特定参数列表和返回类型的方法的引用。它可能包含对象,也可能不包含对象。

    Lambda表达式是匿名函数的一种形式。


    委托是函数签名;就像是

    1
    delegate string MyDelegate(int param1);

    委托没有实现主体。

    lambda是与委托签名匹配的函数调用。对于上述代表,您可以使用以下任何一种:

    1
    2
    3
    (int i) => i.ToString();
    (int i) =>"ignored i";
    (int i) =>"Step" + i.ToString() +" of 10";

    但是,Delegate类型命名错误;创建Delegate类型的对象实际上会创建一个可以容纳函数的变量,这些函数可以是lambda,静态方法或类方法。


    lambda只是委托上的语法糖。编译器最终将lambda转换为委托。

    我相信这些都是一样的:

    1
    2
    Delegate delegate = x =>"hi!";
    Delegate delegate = delegate(object x) { return"hi";};

    委托是函数指针的队列,调用委托可以调用多个方法。 Lambda本质上是一个匿名方法声明,编译器可能会不同地解释它,具体取决于它用作什么上下文。

    您可以通过将lambda表达式转换为委托来获得指向该lambda表达式的委托,或者如果将其作为参数传递给需要特定委托类型的方法,则编译器将为您转换该委托。在LINQ语句中使用它,lambda将由编译器转换为表达式树,而不是简单的委托。

    真正的区别在于,lambda是在另一个表达式内部定义方法的简洁方法,而委托是实际的对象类型。


    代表实际上只是函数的结构化类型。您可以使用名义上的类型输入和实现实现接口或抽象类的匿名类来执行相同的操作,但是当只需要一个函数时,最终将产生大量代码。

    Lambda来自1930年代Alonzo Church的lambda演算的思想。这是创建函数的匿名方式。它们对于组成函数特别有用

    因此,尽管有人可能会说lambda是代表的语法糖,但我会说代表是在c#中使人们轻松使用lambda的桥梁。


    这是我在la脚的博客上放了一段时间的示例。假设您要通过工作线程更新标签。我有4个示例,说明如何使用委托,匿名委托和2种类型的lambda将标签从1更新为50。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
     private void button2_Click(object sender, EventArgs e)
         {
             BackgroundWorker worker = new BackgroundWorker();
             worker.DoWork += new DoWorkEventHandler(worker_DoWork);
             worker.RunWorkerAsync();
         }

         private delegate void UpdateProgDelegate(int count);
         private void UpdateText(int count)
         {
             if (this.lblTest.InvokeRequired)
             {
                 UpdateProgDelegate updateCallBack = new UpdateProgDelegate(UpdateText);
                 this.Invoke(updateCallBack, new object[] { count });
             }
             else
             {
                 lblTest.Text = count.ToString();
             }
         }

         void worker_DoWork(object sender, DoWorkEventArgs e)
         {  
             /* Old Skool delegate usage.  See above for delegate and method definitions */
             for (int i = 0; i < 50; i++)
             {
                 UpdateText(i);
                 Thread.Sleep(50);
             }

             // Anonymous Method
             for (int i = 0; i < 50; i++)
             {
                 lblTest.Invoke((MethodInvoker)(delegate()
                 {
                     lblTest.Text = i.ToString();
                 }));
                 Thread.Sleep(50);
             }

             /* Lambda using the new Func delegate. This lets us take in an int and
              * return a string.  The last parameter is the return type. so
              * So Func<int, string, double> would take in an int and a string
              * and return a double.  count is our int parameter.*/

             Func<int, string> UpdateProgress = (count) => lblTest.Text = count.ToString();
             for (int i = 0; i < 50; i++)
             {
                 lblTest.Invoke(UpdateProgress, i);
                 Thread.Sleep(50);
             }

             /* Finally we have a totally inline Lambda using the Action delegate
              * Action is more or less the same as Func but it returns void. We could
              * use it with parameters if we wanted to like this:
              * Action<string> UpdateProgress = (count) => lblT…*/

             for (int i = 0; i < 50; i++)
             {
                 lblTest.Invoke((Action)(() => lblTest.Text = i.ToString()));
                 Thread.Sleep(50);
             }
         }

    一些基本的。
    "委托"实际上是持有对方法或lambda的引用的变量的名称

    这是一种匿名方法-

    1
    (string testString) => { Console.WriteLine(testString); };

    由于匿名方法没有任何名称,因此我们需要一个委托,我们可以在其中分配这些方法或表达式。对于前

    1
    2
    3
    4
    delegate void PrintTestString(string testString); // declare a delegate

    PrintTestString print = (string testString) => { Console.WriteLine(testString); };
    print();

    与lambda表达式相同。通常我们需要委托来使用它们

    1
    s => s.Age > someValue && s.Age < someValue    // will return true/false

    我们可以使用func委托来使用此表达式。

    1
    2
    3
    Func< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;

    bool result = checkStudentAge ( Student Object);

    我假设您的问题与c#有关,而不与.NET有关,因为您的问题含糊不清,因为.NET并不孤单-也就是说,没有c#-委托和lambda表达式的理解。

    一个(正常的,与所谓的泛型委托相反的委托)委托应该被视为一种函数指针类型的c ++ typedef,例如在c ++中:

    1
    R (*thefunctionpointer) ( T ) ;

    typedef的类型为thefunctionpointer,它是指向函数的指针类型,该函数采用类型为T的对象并返回类型为R的对象。您可以这样使用它:

    1
    2
    thefunctionpointer = &thefunction ;
    R r = (*thefunctionpointer) ( t ) ; // where t is of type T

    其中thefunction是将T并返回R的函数。

    在C#中,您会去

    1
    delegate R thedelegate( T t ) ; // and yes, here the identifier t is needed

    你会这样使用它:

    1
    2
    thedelegate thedel = thefunction ;
    R r = thedel ( t ) ; // where t is of type T

    其中thefunction是将T并返回R的函数。这是给代表的,即所谓的普通代表。

    现在,您在c#中也有泛型委托,这些泛型委托是泛型的委托,即可以说是"模板化"的,因此使用c ++表达式。它们的定义如下:

    1
    public delegate TResult Func<in T, out TResult>(T arg);

    您可以像这样使用它们:

    1
    2
    3
    4
    Func<double, double> thefunctor = thefunction2; // call it a functor because it is
                                                    // really as a functor that you should
                                                    //"see" it
    double y = thefunctor(2.0);

    其中thefunction2是一个以参数为参数并返回double的函数。

    现在,想象一下,我想使用一个现在尚未在任何地方定义的"函数"(而不是thefunction2),并且以后将不再使用。然后,c#允许我们使用此函数的表达式。所谓表达,是指它的"数学"(或功能性,坚持程序)表达,例如:对于double x,我将double x*x关联起来。在数学中,您可以使用" mapsto"乳胶符号来编写此代码。在c#中,已借用了功能符号:=>。例如 :

    1
    2
    Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
                                                               // mandatory

    (double x) => x * x是一个表达式。它不是类型,而代理(泛型或非泛型)则是。

    道德?最后,如果不是函数指针类型(封装的+智能+泛型函数指针的类型),那么委托是什么(分别是通用委托),是吗?还有别的!看到这个和那个。


    Lambda是代表的简化版本。它们具有闭包的某些属性,例如匿名委托,但是还允许您使用隐式键入。这样的lambda:

    1
    something.Sort((x, y) => return x.CompareTo(y));

    比您可以对委托执行的操作更为简洁:

    1
    2
    3
    4
    5
    6
    7
    something.Sort(sortMethod);
    ...

    private int sortMethod(SomeType one, SomeType two)
    {
        one.CompareTo(two)
    }


    好吧,真正简化的版本是lambda只是匿名函数的简写。委托不仅可以执行匿名功能,还可以做很多事情:事件,异步调用和多个方法链。


    推荐阅读