.NET Framework中lambda和委托有什么区别?

时间:2008-09-16 14:55:06

标签: c# .net lambda

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

18 个答案:

答案 0 :(得分:83)

它们实际上是两个非常不同的东西。 “Delegate”实际上是包含对方法或lambda的引用的变量的名称,而lambda是没有永久名称的方法。

除了一些微妙的差异外,Lambda与其他方法非常相似。

  1. 普通方法在"statement"中定义并且与永久名称相关联,而lambda在"expression"中“在运行中”定义,并且没有永久名称。
  2. 一些lambda可以与.NET表达式树一起使用,而方法则不能。
  3. 委托的定义如下:

    delegate Int32 BinaryIntOp(Int32 x, Int32 y);
    

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

    lambda可能定义如下:

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

    另外需要注意的是,虽然通用的Func和Action类型通常被认为是“lambda类型”,但它们就像任何其他委托一样。关于它们的好处是它们实际上为您可能需要的任何类型的委托定义了一个名称(最多4个参数,但您当然可以添加更多自己的参数)。因此,如果您使用各种各样的委托类型,但只能使用一次,则可以使用Func和Action避免使用委托声明来混淆代码。

    以下是Func和Action如何“不只是为了lambdas”的例证:

    Int32 DiffOfSquares(Int32 x, Int32 y)
    {
      return x*x - y*y;
    }
    
    Func<Int32, Int32, Int32> funcPtr = DiffOfSquares;
    

    另一个有用的事情是,具有相同签名但名称不同的委托类型(不是方法本身)不会隐式地相互转换。这包括Func和Action代理。但是,如果签名相同,则可以在它们之间明确地进行转换。

    加倍努力....在C#中,函数是灵活的,使用lambdas和delegates。但是C#没有“一流的功能”。您可以使用分配给委托变量的函数名称来创建表示该函数的对象。但它确实是一个编译技巧。如果您通过编写函数名称后跟一个点来启动语句(即尝试对函数本身进行成员访问),您将发现没有可供参考的成员。甚至不是来自Object的那些。这可以防止程序员做一些有用的(当然也可能是危险的)诸如添加可以在任何函数上调用的扩展方法。你可以做的最好的事情是扩展Delegate类本身,这肯定也很有用,但不是很多。

    更新:另请参阅Karg's answer,说明匿名代表与方法之间的区别&amp; lambda表达式。

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

答案 1 :(得分:28)

这个问题有点含糊不清,这解释了你得到的答案的广泛差异。

你实际上问过.NET框架中lambda和委托之间的区别;这可能是众多事情之一。你在问:

  • 使用C#(或VB.NET)语言,lambda表达式和匿名委托有什么区别?

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

  • 或者在这些极端之间或附近的某个地方?

有些人似乎试图回答“C#Lambda表达式和.NET System.Delegate之间的区别是什么?”这个问题的答案,这并没有多大意义。

.NET框架本身并不理解匿名委托,lambda表达式或闭包的概念 - 这些都是由语言规范定义的。想想C#编译器如何将匿名方法的定义转换为生成的类上的方法,并使用成员变量来保持闭包状态;到.NET,没有关于委托的匿名;对于编写它的C#程序员来说,它只是匿名的。对于分配给委托类型的lambda表达式同样如此。

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

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

在C#中,您可以通过将lambda表达式赋值给所述类型的变量来生成System.Expressions.Expression类型的实例,这将生成在运行时构造表达式的相应代码。

当然,如果你 询问lambda表达式和C#中的匿名方法有什么区别,那么所有这些都非常相关,在这种情况下,主要区别在于简洁当你不关心参数而不计划返回一个值时,它倾向于匿名委托,当你想要类型推理参数和返回类型时,它倾向于lambdas。

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

答案 2 :(得分:18)

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

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

你可以用以下四种方式调用它(注意第二行有一个没有任何参数的匿名委托):

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表达式或没有参数的方法。这些是不允许的:

Test(() => String.Empty); //Not allowed, lambda must match signature
Test(D2); //Not allowed, method must match signature

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

答案 3 :(得分:13)

委托等同于函数指针/方法指针/回调(接受你的选择),lambdas是非常简化的匿名函数。至少那是我告诉别人的。

答案 4 :(得分:3)

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

答案 5 :(得分:3)

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

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

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

答案 6 :(得分:2)

委托是一个功能签名;

之类的东西
delegate string MyDelegate(int param1);

代表没有实体。

lambda是一个与委托签名匹配的函数调用。对于上述代表,您可以使用;

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

Delegate类型名称很糟糕;创建一个Delegate类型的对象实际上创建了一个可以保存函数的变量 - 无论是lambda,静态方法还是类方法。

答案 7 :(得分:2)

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

我相信这些都是一样的:

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

答案 8 :(得分:2)

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

lambda表达式是一种匿名函数。

答案 9 :(得分:2)

很明显,问题是“lambdas和匿名代表之间有什么区别?”在这里的所有答案中,只有一个人做对了 - 主要区别在于lambdas可以用来创建表达式树和代表。

您可以在MSDN上阅读更多内容:http://msdn.microsoft.com/en-us/library/bb397687.aspx

答案 10 :(得分:1)

代表实际上只是功能的结构类型。你可以通过名义输入和实现一个实现接口或抽象类的匿名类来做同样的事情,但是当只需要一个函数时,最终会产生很多代码。

Lambda来自20世纪30年代Alonzo Church的lambda演算。这是一种创建函数的匿名方式。它们对于编写函数特别有用

因此,虽然有些人可能会说lambda是代表们的语法糖,但我会说委托是在c#中将人们变成lambdas的桥梁。

答案 11 :(得分:1)

委托是函数指针的队列,调用委托可以调用多个方法。 lambda本质上是一个匿名方法声明,编译器可能会对其进行不同的解释,具体取决于它所使用的上下文。

您可以通过将lambda表达式转换为委托来获取指向lambda表达式的委托,或者将其作为参数传递给期望特定委托类型的方法,编译器将为您强制转换它。在LINQ语句中使用它,lambda将由编译器转换为表达式树而不仅仅是委托。

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

答案 12 :(得分:0)

Lambdas是委托的简化版本。它们具有closure类似匿名委托的一些属性,但也允许您使用隐含类型。像这样的lambda:

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

比委托代表的内容简洁得多:

something.Sort(sortMethod);
...

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

答案 13 :(得分:0)

这是我在我的蹩脚博客上放了一段时间的例子。假设您想要从工作线程更新标签。我有4个如何使用委托,匿名委托和2种lambda将标签从1更新到50的例子。

 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); 
         } 
     }

答案 14 :(得分:0)

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

正常,与所谓的通用委托相对, cf 以后)委托应被视为一种c ++ {{ 1}}函数指针类型,例如在c ++中:

typedef

typedef是类型R (*thefunctionpointer) ( T ) ; ,它是指向类型为thefunctionpointer的对象并返回类型为T的对象的函数的指针类型。你可以这样使用它:

R

其中thefunctionpointer = &thefunction ; R r = (*thefunctionpointer) ( t ) ; // where t is of type T 是一个功能thefunction并返回T

在c#中你会选择

R

你会像这样使用它:

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

其中thedelegate thedel = thefunction ; R r = thedel ( t ) ; // where t is of type T 是一个功能thefunction并返回T。这适用于代表,即所谓的普通代表。

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

R

你可以像这样使用它们:

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

其中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我想使用一个现在没有定义的“函数”,一个语句,我以后永远不会使用。然后c#允许我们使用此函数的表达式。通过表达我的意思是“数学”(或功能,坚持程序)表达它,例如:到thefunction2我将关联 double x {{1 }}。在数学中,你使用the "\mapsto" latex symbol来写这个。在c#中,函数符号已被借用:double。例如:

x*x

=>expression。它不是一种类型,而代表(通用或非通用)是。

道德?最后,什么是委托(resp。泛型委托),如果不是函数指针类型(resp。包装+智能+泛型函数指针类型),是吗?还有别的!请参阅thisthat

答案 15 :(得分:0)

这里有一些基础。 &#34;代表&#34;实际上是包含对方法或lambda的引用的变量的名称

这是一种匿名方法 -

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

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

delegate void PrintTestString(string testString); // declare a delegate

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

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

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

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

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

bool result = checkStudentAge ( Student Object);

答案 16 :(得分:0)

简短版本:

委托是一种类型,表示对方法的引用。 C# lambda 表达式是一种创建委托表达式树的语法。

有点长的版本:

委托不是公认的答案中所说的“变量的名称”。

委托是表示对方法 (docs.microsoft.com) 的引用的类型(字面意思是一种类型,如果您检查 IL,它就是一个类)。

enter image description here

可以启动此类型以将其实例与具有兼容签名和返回类型的任何方法相关联。

namespace System
{
    // define a type
    public delegate TResult Func<in T, out TResult>(T arg);
}

// method with the compatible signature
public static bool IsPositive(int int32)
{
    return int32 > 0;
}

// initiated and associate
Func<int, bool> isPositive = new Func<int, bool>(IsPositive);

C# 2.0 引入了一种语法糖,即匿名方法,使方法能够内联定义。

Func<int, bool> isPositive = delegate(int int32)
{
    return int32 > 0;
};

在 C# 3.0+ 中,上述匿名方法的内联定义可以通过 lambda 表达式进一步简化

Func<int, bool> isPositive = (int int32) =>
{
    return int32 > 0;
};

C# lambda 表达式是一种创建委托表达式树的语法。我相信表达式树不是这个问题的主题 (Jamie King about expression trees)。

可以找到更多here

答案 17 :(得分:-1)

嗯,真正过于简单的版本是lambda只是匿名函数的简写。委托可以做的不仅仅是匿名函数:事件,异步调用和多个方法链。