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

时间:2020-03-05 18:56:12  来源:igfitidea点击:

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

解决方案

回答

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

回答

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

回答

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

回答

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

我相信这些都是一样的:

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

回答

委托是函数签名;就像是

delegate string MyDelegate(int param1);

委托没有实现主体。

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

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

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

回答

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

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

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

回答

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

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

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

回答

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

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

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

something.Sort(sortMethod);
...

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

回答

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

回答

它们实际上是两个截然不同的事物。 "委托"实际上是持有对方法或者lambda的引用的变量的名称,而lambda是没有永久名称的方法。

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

  • 普通方法在"语句"中定义,并与一个永久名称相关联,而lambda在"表达式"中"即时"定义,并且没有永久名称。
  • 某些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如何"不仅仅用于lambda"的说明:

Int32 DiffOfSquares(Int32 x, Int32 y)
{
  return x*x - y*y;
}

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

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

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

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

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

回答

这是我在la脚的博客上发表过的例子。假设我们要通过工作线程更新标签。我有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); 
         } 
     }

回答

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

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

回答

委托是函数指针的队列,调用委托可以调用多个方法。 Lambda本质上是一个匿名方法声明,根据其使用的上下文,编译器可能会不同地解释它。

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

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

回答

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

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

  • Lambda表达式和C#(或者VB.NET)语言中的匿名委托之间有什么区别?
  • .NET 3.5中的System.Linq.Expressions.LambdaExpression对象和System.Delegate对象之间有什么区别?
  • 或者介于这些极端之间或者周围的某个地方?

有些人似乎试图为我们回答" CLambda表达式与.NET System.Delegate之间有什么区别?"的问题,但这没有什么意义。

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

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

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

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

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

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