了解Lambda表达式和委托

时间:2010-07-19 14:43:19

标签: c# .net-3.5 delegates lambda

我一直试图解决这个问题很长一段时间(阅读在线博客和关键词),但到目前为止还没有成功。

什么是代表?什么是Lambda表达式?两者的优点和缺点?何时使用其中一种可能的最佳做法?

提前致谢。

6 个答案:

答案 0 :(得分:28)

委托是可以用作变量的方法,比如字符串等。例如,您可以使用一个参数声明委托方法:

delegate void OneArgumentDelegate(string argument);

它没有做任何事情,就像一个界面。如果你在任何类中有一个方法,有一个这样的参数:

void SomeMethod(string someArgument) {}

匹配委托的签名,因此可以分配给其类型的变量:

OneArgumentDelegate ThisIsAVariable = new OneArgumentDelegate(SomeMethod);
OneArgumentDelegate ThisIsAlsoAVariable = SomeMethod; // Shorthand works too

然后可以将这些作为参数传递给方法并调用,如下所示:

void Main()
{
  DoStuff(PrintString);
}

void PrintString(string text)
{
  Console.WriteLine(text);
}

void DoStuff(OneArgumentDelegate action) 
{
  action("Hello!");
}

这将输出Hello!

Lambda表达式是DoStuff(PrintString)的简写,因此您不必为要使用的每个委托变量创建一个方法。您'创建'传递给方法的临时方法。它的工作原理如下:

DoStuff(string text => Console.WriteLine(text)); // single line
DoStuff(string text => // multi line
{
  Console.WriteLine(text);
  Console.WriteLine(text);
});

Lambda表达式只是一种简写,你也可以创建一个单独的方法并传递它。我希望你现在能更好地理解它; - )

答案 1 :(得分:6)

委托是一个包含对函数的引用的对象。几个不同的代表可能指向相同的功能。 委托的类型定义了它可能指向的函数的足迹。

Lambda表达式是一个没有名称的函数。执行此功能的唯一方法是让委托指向该功能。 Lambda表达式通常定义在需要委托给具有给定足迹的函数的位置。这对于使代码更简洁,同时更具描述性和灵活性非常有用

我建议你使用一个命名函数和一个委托,只要你有一些代码将从不同的地方调用。一个常见示例是您要附加到多个事件生成器的事件侦听器。

考虑编写单独函数的另一点是代码的复杂性。如果你在lambda表达式中编写一个完整的程序,那对任何人都无济于事。

另一方面,您经常需要一些想要以回调方式执行的简单处理。这就是你可能喜欢 lambda表达式的地方。

lambda表达式非常好,它们继承了它们定义的范围,因此您可以轻松地将您的变量放在lambda表达式中,从而传递大量信息。你应该小心,请参阅备注部分 this文章。

Labdas与LINQ合作非常出色。

总而言之,我必须引用yet another必读的msdn部分:

当您使用基于方法的语法来调用Enumerable类中的Where方法时(就像在LINQ to Objects和LINQ to XML中那样),参数是委托类型System.Func。 lambda表达式是创建该委托的最便捷方式。当你在例如System.Linq.Queryable类中调用相同的方法时(就像在LINQ to SQL中那样),那么参数类型是一个System.Linq.Expressions.Expression,其中Func是任何Func委托,最多16个输入参数。同样,lambda表达式只是构造表达式树的一种非常简洁的方式。 lambdas允许Where调用看起来类似,尽管实际上从lambda创建的对象类型是不同的。

答案 2 :(得分:3)

没有人提到过匿名代表。您可以动态创建委托,而无需声明它们:

public void Action(Func<int, int> func);
...
Action(delegate(int x) { return x*x; });

这只是lambda语法的一个更详细的版本:

Action(x => x*x);

另请注意,lambda语法具有更积极的类型推断。另一个区别是lambda表示法可用于声明表达式树:

public void Action(Expression<Func<int, int>>);
Action(x => x*x);

在这种情况下,您获得的不是函数,而是可以在运行时检查的解析树。例如,这就是linq查询构建sql的方式。

修改

更直接地回答何时使用其中一个的问题:

您很少需要自己声明新的委托类型,尽管偶尔会有所帮助。该框架提供了多种Func<>类型,以及Action<T>Predicate<T>,这些通常都是您需要的。

在“动态”创建函数时,使用匿名委托语法而不是lambda语法没有任何好处。由于lambda语法更简洁且类型推断,所以更喜欢它。

答案 3 :(得分:2)

Delegate只是指向函数的指针。它就像一个“变量”,你可以将地址保存到另一个名为

的函数中
    public class test {
    Action<int> CallUserCode;

    public test(Action<int> proc){
        CallUserCode = proc;
    }

    void foo(){
        int someValue = 0;
        //do some stuff that needs to call the user procedure
        CallUserCode(someValue);
    }
}

Lambda表达式也是一个委托,它具有简化的语法,可以“创建”“内联”函数。 因此,前面的示例将使用lambda以下列方式调用。

void bar(){
    var t = new test(x => { /* do something with the value i get from foo */});
    t.foo();  //here function foo gets called, which will call 'do something' AND call my lambda expression
}

答案 4 :(得分:2)

有一个重要的区别是我们可以使用lamda而不是委托。

private delegate int emptymethoddel();
// Delegate for method with no params and returns int

等效的框架委托类型是:Func<int>

但是你不能从参数化方法创建新的委托实例/ func。

private int TwoArgMethod(int i, int j)
{
    return i + j;
}

但是,使用lambda,您可以获得上述方法的委托。

Func<int> retmethod = () => TwoArgMethod(10, 20);

但是对于委托实例化,我们不能这样做

emptymethoddel retmethod4 = new emptymethoddel(TwoArgMethod(10,20)); 
// mismatch method signature

使用lambda,我们可以获得指向与“Func”或任何其他变体不匹配的方法的指针。

答案 5 :(得分:1)

正如其他人所说,lambdas是一种内联和匿名创建委托的语法。使用传统功能无法实现的lambda可以做的一件事就是闭包。这样,您就可以在运行时使用运行时信息创建函数:

string mystring = SomeObject.GetMyString();
AnotherObject.OnSomeEvent += (eventparams => 
{
  string newstring = string.Format(eventparams.Message, mystring);
  SomeService.PrintEvent(newstring);
}

这样,mystring被合并到委托中,可以用作变量。