.NET Framework 中的 lambda 和委托有啥区别?

Posted

技术标签:

【中文标题】.NET Framework 中的 lambda 和委托有啥区别?【英文标题】:What is the difference between lambdas and delegates in the .NET Framework?.NET Framework 中的 lambda 和委托有什么区别? 【发布时间】:2010-09-09 13:11:06 【问题描述】:

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

【问题讨论】:

“委托”是指委托类型还是匿名委托?它们也不同。 delegate keyword vs. lambda notation 的可能重复项 为什么人们把他的问题弄得这么复杂?只需回答什么是委托,什么是 lambda。尽可能多地解释,让他选择适合他的东西。 【参考方案1】:

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

Lambdas 与其他方法非常相似,除了一些细微的差别。

    普通方法在 "statement" 中定义并绑定到永久名称,而 lambda 在 "expression" 中“动态”定义且没有永久名称。 某些 lambda 可以与 .NET 表达式树一起使用,而方法则不能。

委托是这样定义的:

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# 中,函数很灵活,可以使用 lambda 和委托。但是 C# 没有“一流的功能”。您可以使用分配给委托变量的函数名称来实质上创建一个表示该函数的对象。但这确实是一个编译器技巧。如果您通过编写函数名称后跟一个点来开始声明(即尝试对函数本身进行成员访问),您会发现那里没有可引用的成员。甚至不是来自 Object 的那些。这可以防止程序员做有用的(当然也可能是危险的)事情,例如添加可以在任何函数上调用的扩展方法。您可以做的最好的事情是扩展 Delegate 类本身,这当然也很有用,但作用不大。

更新:另请参阅 Karg's answer,说明匿名委托与方法和 lambda 之间的区别。

更新 2:James Hart 重要但非常技术性,请注意 lambda 和委托不是 .NET 实体(即 CLR 没有委托或 lambda 的概念),而是框架和语言构造。

【讨论】:

很好的解释。尽管我认为您的意思是“一流的功能”,而不是“一流的对象”。 :) 你是对的。在写作过程中,我的句子结构不同(“C# 函数实际上不是一流的对象”)并且忘记了改变它。谢谢! 一个普通的方法被定义在一个“语句”中 一个语句是一个命令式程序序列中的一个动作,可能基于一个表达式。方法定义不是不同的语法结构吗? docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/… 中未列出方法定义【参考方案2】:

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

您实际上问过 .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 表达式支持表达式生成。

【讨论】:

好消息!你启发了我启动反射器并查看 IL。我不知道 lambdas 会导致生成的类,但现在我想它是完全有道理的。【参考方案3】:

一个区别是匿名委托可以省略参数,而 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;

【讨论】:

【参考方案4】:

委托等价于函数指针/方法指针/回调(随你选择),而 lambda 是非常简化的匿名函数。至少我是这么告诉人们的。

【讨论】:

没错!没有区别”。它们是两个本质上不同的东西。【参考方案5】:

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

【讨论】:

【参考方案6】:

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

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

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

【讨论】:

的确如此,但它们之间的区别在于它们是两个完全不同的概念。这就像比较苹果和橘子。请参阅 Dan Shield 的回答。【参考方案7】:

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

这些都是一样的,我相信:

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

【讨论】:

这些例子都不能编译。即使您将 Delegate 实例的名称从 'delegate' 更改为关键字。【参考方案8】:

委托是一个函数签名;像

delegate string MyDelegate(int param1);

委托没有实现主体。

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

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

不过,Delegate 类型的名字很糟糕;创建Delegate 类型的对象实际上会创建一个可以保存函数的变量——可以是 lambda、静态方法或类方法。

【讨论】:

当您创建 MyDelegate 类型的变量时,它实际上并不是运行时类型。运行时类型是委托。委托、lambda 和表达式树的编译方式涉及编译器技巧,我认为这会导致代码暗示不正确的事情。【参考方案9】:

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

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

【讨论】:

【参考方案10】:

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

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

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

【讨论】:

【参考方案11】:

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

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

【讨论】:

【参考方案12】:

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

Lambda 源自 1930 年代 Alonzo Church 的 lambda 演算概念。它是一种创建函数的匿名方式。它们对于组合函数特别有用

因此,虽然有些人可能会说 lambda 是代表的语法糖,但我想说的是,delegate 是让人们轻松进入 c# 中的 lambda 的桥梁。

【讨论】:

【参考方案13】:

这里有一些基本的。 “Delegate”实际上是保存对方法或 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);

【讨论】:

【参考方案14】:

Lambda 是委托的简化版本。它们具有closure 的一些属性,例如匿名委托,而且还允许您使用隐含类型。像这样的 lambda:

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

比使用委托更简洁:

something.Sort(sortMethod);
...

private int sortMethod(SomeType one, SomeType two)

    one.CompareTo(two)

【讨论】:

你的意思是 lambdas 就像简化的匿名方法(不是委托)。像方法(匿名或非匿名)一样,它们可以分配给委托变量。【参考方案15】:

这是我在我的蹩脚博客上放了一段时间的例子。假设您想从工作线程更新标签。我有 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); 
          
     

【讨论】:

【参考方案16】:

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

A(normal,与所谓的 generic 委托相反,cf 稍后)委托应该被视为一种 c++ @987654325函数指针类型的@,例如在c++中:

R (*thefunctionpointer) ( T ) ;

typedef 是thefunctionpointer 类型,它是指向一个函数的指针类型,该函数采用T 类型的对象并返回R 类型的对象。你会这样使用它:

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

其中thefunction 将是一个函数,接受T 并返回R

在 c# 中你会选择

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 并返回R。这适用于委托,即所谓的普通委托。

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

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 的函数。

现在想象一下,不是thefunction2,我想使用一个现在没有定义的“函数”,通过一个声明,我以后永远不会使用它。然后c#允许我们使用这个函数的表达式。表达式我的意思是它的“数学”(或函数,坚持程序)表达式,例如:double x 我将关联doublex*x。在数学中,您使用the "\mapsto" latex symbol 编写此内容。在 c# 中,函数符号是借用的:=&gt;。例如:

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

(double x) =&gt; x * x 是一个expression。它不是一种类型,而委托(无论是否泛型)是。

道德?最后,什么是委托(分别是泛型委托),如果不是函数指针类型(分别是包装+智能+泛型函数指针类型),嗯?别的东西!请参阅this 和that。

【讨论】:

【参考方案17】:

短版:

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

有点长的版本:

Delegate 不是接受的答案中所说的“变量的名称”。

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

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

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。

【讨论】:

【参考方案18】:

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

【讨论】:

lambda 可以用作事件处理程序; button.Click += (sender, eventArgs) => MessageBox.Show("Click");并异步调用 new System.Threading.Thread(() => Console.Write("Executed on a thread")).Start();

以上是关于.NET Framework 中的 lambda 和委托有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

在未安装 .Net Framework 的情况下运行 c# 3 应用程序?

使用 lambda 表达式在 Entity Framework 的实体上动态应用过滤器

如何使用 Entity Framework Core 2.0 上的 lambda 语法在 LINQ 中实现 LEFT OUTER JOIN?

Entity Framework lambda 扩展方法在啥时候将查询发送到数据库?

.NET 6 中的 .NET Framework 4.5 / .NET Standard 1.0 支持

.net Framework 中的四种计时器