我经常被问到这个问题,我想我会征求一些关于如何最好地描述差异的意见。
18 回答
它们实际上是两个非常不同的东西。“委托”实际上是保存对方法或 lambda 的引用的变量的名称,而 lambda 是没有永久名称的方法。
Lambda 与其他方法非常相似,除了一些细微的差别。
委托定义如下:
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 的回答,说明匿名代表与方法和 lambda 之间的区别。
更新 2:James Hart提出了一个重要但非常技术性的问题,请注意 lambda 和委托不是 .NET 实体(即 CLR 没有委托或 lambda 的概念),而是它们是框架和语言构造。
这个问题有点模棱两可,这解释了你得到的答案的巨大差异。
您实际上问过 .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所理解的是委托的概念 - 一种描述方法签名的类型,其实例表示对特定对象上特定方法的绑定调用,或对特定类型上可以调用的特定方法的未绑定调用该类型的任何对象,其中所述方法遵循所述签名。这些类型都继承自 System.Delegate。
.NET 3.5 还引入了 System.Linq.Expressions 命名空间,其中包含用于描述代码表达式的类,因此还可以表示对特定类型或对象的方法的绑定或非绑定调用。然后可以将 LambdaExpression 实例编译为实际的委托(由此对基于表达式结构的动态方法进行代码生成,并返回指向它的委托指针)。
在 C# 中,您可以通过将 lambda 表达式分配给所述类型的变量来生成 System.Expressions.Expression 类型的实例,这将生成适当的代码以在运行时构造表达式。
当然,如果你问 C# 中的 lambda 表达式和匿名方法之间有什么区别,那么所有这些都几乎无关紧要,在这种情况下,主要区别是简洁,当你不这样做时,它倾向于匿名委托t 关心参数并且不打算返回一个值,并且当你想要类型推断参数和返回类型时使用 lambdas。
并且 lambda 表达式支持表达式生成。
一个区别是匿名委托可以省略参数,而 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 可以变成一个委托,但它也可以变成一个 LINQ 表达式树。例如,
Func<int, int> f = x => x + 1;
Expression<Func<int, int>> exprTree = x => x + 1;
第一行产生一个委托,而第二行产生一个表达式树。
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
实际上会创建一个可以保存函数的变量——无论是 lambdas、静态方法还是类方法。
委托是对具有特定参数列表和返回类型的方法的引用。它可能包含也可能不包含对象。
lambda 表达式是匿名函数的一种形式。
一个委托是一个函数指针队列,调用一个委托可能会调用多个方法。lambda 本质上是一个匿名方法声明,编译器可能会对它进行不同的解释,具体取决于它用作什么上下文。
您可以通过将 lambda 表达式作为方法转换为委托来获取一个委托,或者如果将其作为参数传递给需要特定委托类型的方法,编译器将为您转换它。在 LINQ 语句中使用它,编译器会将 lambda 转换为表达式树,而不是简单的委托。
真正的区别在于 lambda 是在另一个表达式中定义方法的简洁方式,而委托是实际的对象类型。
很明显,这个问题的意思是“lambdas 和匿名代表之间有什么区别?” 在这里的所有答案中,只有一个人做对了——主要区别在于 lambdas 可用于创建表达式树以及委托。
您可以在 MSDN 上阅读更多内容:http: //msdn.microsoft.com/en-us/library/bb397687.aspx
委托实际上只是函数的结构类型。你可以用名义类型来做同样的事情,并实现一个实现接口或抽象类的匿名类,但是当只需要一个函数时,这最终会变成很多代码。
Lambda 源自 1930 年代 Alonzo Church 的 lambda 演算思想。它是一种创建函数的匿名方式。它们对于组合函数特别有用
因此,虽然有些人可能会说 lambda 是代表的语法糖,但我会说代表是让人们轻松进入 c# 中的 lambda 的桥梁。
这里有一些基本的。“委托”实际上是保存对方法或 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);
Lambda 是委托的简化版本。它们具有闭包的一些属性,例如匿名委托,但也允许您使用隐含类型。像这样的 lambda:
something.Sort((x, y) => return x.CompareTo(y));
比您可以对委托执行的操作要简洁得多:
something.Sort(sortMethod);
...
private int sortMethod(SomeType one, SomeType two)
{
one.CompareTo(two)
}
这是我在我的蹩脚博客上放了一段时间的例子。假设您想从工作线程更新标签。我有 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);
}
}
我假设您的问题涉及 c# 而不是 .NET,因为您的问题含糊不清,因为 .NET 并不孤单 - 也就是说,没有 c# - 对委托和 lambda 表达式的理解。
一个(正常的,与所谓的泛型委托相反,cf稍后)委托应该被视为一种typedef
函数指针类型的 c++,例如在 c++ 中:
R (*thefunctionpointer) ( T ) ;
typedef 的类型thefunctionpointer
,它是指向一个函数的指针类型,该函数接受一个类型的对象T
并返回一个类型的对象R
。你会像这样使用它:
thefunctionpointer = &thefunction ;
R r = (*thefunctionpointer) ( t ) ; // where t is of type T
wherethefunction
将是一个接受 aT
并返回a 的函数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
wherethefunction
将是一个接受 aT
并返回a 的函数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);
wherethefunction2
是一个作为参数并返回 a 的函数double
。
现在想象一下,thefunction2
我想使用一个现在没有定义的“函数”,而不是一个声明,我以后永远不会使用它。然后c#允许我们使用这个函数的表达式。表达我的意思是它的“数学”(或功能,坚持程序)表达,例如:double x
我将把. double
x*x
在数学中,您使用"\mapsto" 乳胶符号来编写它。在 c# 中,函数符号是借用的 : =>
。例如 :
Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
// mandatory
(double x) => x * x
是一个表达式。它不是一种类型,而委托(通用或非通用)是。
道德?最后,什么是委托(分别是泛型委托),如果不是函数指针类型(分别是包装+智能+泛型函数指针类型),嗯?别的东西!看到这个那个。_
简洁版本:
委托是一种表示对方法的引用的类型。C# lambda表达式是一种用于创建委托或表达式树的语法。
有点长的版本:
正如在接受的答案中所说,委托不是“变量的名称”。
委托是表示对方法 ( docs.microsoft.com ) 的引用的类型(字面意思是类型,如果您检查 IL,它是一个类)。
可以启动此类型以将其实例与具有兼容签名和返回类型的任何方法相关联。
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 关于表达式树)。
更多可以在这里找到。
好吧,真正过于简化的版本是 lambda 只是匿名函数的简写。委托可以做的不仅仅是匿名函数:诸如事件、异步调用和多个方法链之类的事情。