关于c#3.0:C#Lambda表达式:我为什么要使用它们?

C# Lambda expressions: Why should I use them?

我已经快速阅读了Microsoft lambda表达式文档。

这种例子帮助我更好地理解了:

1
2
3
delegate int del(int i);
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25

不过,我不明白为什么这是一个创新。它只是一个在"方法变量"结束时死亡的方法,对吗?为什么我要用这个来代替真正的方法?


lambda表达式是匿名委托的简单语法,可以在任何可以使用匿名委托的地方使用。然而,相反的情况并非如此;lambda表达式可以转换为表达式树,这允许像linq-to-sql这样的魔力。

下面是一个使用匿名委托然后使用lambda表达式的linq-to-objects表达式的示例,以显示它们在视觉上有多容易:

1
2
3
4
5
6
7
8
9
10
11
// anonymous delegate
var evens = Enumerable
                .Range(1, 100)
                .Where(delegate(int x) { return (x % 2) == 0; })
                .ToList();

// lambda expression
var evens = Enumerable
                .Range(1, 100)
                .Where(x => (x % 2) == 0)
                .ToList();

lambda表达式和匿名委托比编写单独的函数有一个优势:它们实现闭包,允许您将本地状态传递给函数,而无需向函数添加参数或创建一次性使用的对象。

表达式树是C 3.0的一个非常强大的新特性,它允许API查看表达式的结构,而不只是获取可以执行的方法的引用。API只需将委托参数转换为Expression参数,编译器将从lambda而不是匿名委托生成表达式树:

1
void Example(Predicate<int> aDelegate);

叫作:

1
Example(x => x > 5);

变成:

1
void Example(Expression<Predicate<int>> expressionTree);

后者将传递描述表达式x > 5的抽象语法树的表示。Linq to SQL依赖于此行为,以便能够将C表达式转换为服务器端筛选/排序等所需的SQL表达式。


匿名函数和表达式对于一次性方法很有用,因为这些方法不能从创建完整方法所需的额外工作中获益。

考虑这个例子:

1
 string person = people.Find(person => person.Contains("Joe"));

对战

1
2
3
4
5
6
7
 public string FindPerson(string nameContains, List<string> persons)
 {
     foreach (string person in persons)
         if (person.Contains(nameContains))
             return person;
     return null;
 }

它们在功能上是等效的。


当我想使用另一个控件为某个控件的事件声明一个处理程序时,我发现它们非常有用。要正常地执行此操作,您必须将控件的引用存储在类的字段中,这样您就可以在创建控件的不同方法中使用它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private ComboBox combo;
private Label label;

public CreateControls()
{
    combo = new ComboBox();
    label = new Label();
    //some initializing code
    combo.SelectedIndexChanged += new EventHandler(combo_SelectedIndexChanged);
}

void combo_SelectedIndexChanged(object sender, EventArgs e)
{
    label.Text = combo.SelectedValue;
}

多亏了lambda表达式,您可以这样使用它:

2

容易得多。


lambda清理了c 2.0的匿名委托语法…例如

1
Strings.Find(s => s =="hello");

在C 2.0中完成,如下所示:

1
Strings.Find(delegate(String s) { return s =="hello"; });

在功能上,它们做的是完全相同的事情,这只是一个更简洁的语法。


这只是使用lambda表达式的一种方法。可以在任何可以使用委托的地方使用lambda表达式。这允许您执行以下操作:

2

此代码将在列表中搜索与单词"hello"匹配的条目。另一种方法是将委托实际传递给find方法,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<string> strings = new List<string>();
strings.Add("Good");
strings.Add("Morning")
strings.Add("Starshine");
strings.Add("The");
strings.Add("Earth");
strings.Add("says");
strings.Add("hello");

private static bool FindHello(String s)
{
    return s =="hello";
}

strings.Find(FindHello);

编辑:

在C 2.0中,可以使用匿名委托语法:

1
  strings.Find(delegate(String s) { return s =="hello"; });

lambda明显清除了这种语法。


Microsoft为我们提供了一种更干净、更方便的方法来创建名为lambda表达式的匿名委托。然而,对于这个语句的表达式部分并没有太多的关注。Microsoft发布了整个命名空间System.Linq.Expressions,其中包含基于lambda表达式创建表达式树的类。表达式树由表示逻辑的对象组成。例如,x=y+z是一个表达式,它可能是.NET中表达式树的一部分。请考虑以下(简单)示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
using System.Linq;
using System.Linq.Expressions;


namespace ExpressionTreeThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            Expression<Func<int, int>> expr = (x) => x + 1; //this is not a delegate, but an object
            var del = expr.Compile(); //compiles the object to a CLR delegate, at runtime
            Console.WriteLine(del(5)); //we are just invoking a delegate at this point
            Console.ReadKey();
        }
    }
}

这个例子很简单。我确信您在想,"这是无用的,因为我可以直接创建委托,而不是创建表达式并在运行时编译它"。你是对的。但这为表达树提供了基础。表达式命名空间中有许多可用的表达式,您可以构建自己的表达式。我认为您可以看到,当您不知道算法在设计或编译时应该是什么时,这可能很有用。我在某个地方看到了一个用这个编写科学计算器的例子。您也可以将其用于贝叶斯系统,或者用于遗传编程(ai)。在我的职业生涯中,有几次我不得不编写类似Excel的功能,允许用户输入简单的表达式(加法、分式运算等)来操作可用的数据。在.NET 3.5之前的版本中,我不得不使用一些C语言之外的脚本语言,或者不得不使用反射中的代码发出功能来动态创建.NET代码。现在我将使用表达式树。


它省去了在一个特定的地方只使用一次的方法,而不需要在远离使用它们的地方定义它们。好的用途是作为诸如排序之类的通用算法的比较器,然后您可以定义一个自定义排序函数,在该函数中调用排序,而不是进一步强制您查找其他位置以查看排序所依据的内容。

这并不是真正的创新。Lisp有lambda函数大约30年或更长时间了。


您还可以在编写通用代码以对方法进行操作时找到lambda表达式的用法。

例如:用于计算方法调用所用时间的泛型函数。(即这里的Action)

1
2
3
4
5
6
7
8
public&nbsp;static&nbsp;long&nbsp;Measure(Action&nbsp;action)
{
    Stopwatch&nbsp;sw&nbsp;=&nbsp;new&nbsp;Stopwatch();
    sw.Start();
    action();
    sw.Stop();
    return&nbsp;sw.ElapsedMilliseconds;
}

您可以使用lambda表达式调用上面的方法,如下所示,

1
var&nbsp;timeTaken&nbsp;=&nbsp;Measure(()&nbsp;=>&nbsp;yourMethod(param));

表达式允许您从方法和out参数中获取返回值

1
var&nbsp;timeTaken&nbsp;=&nbsp;Measure(()&nbsp;=>&nbsp;returnValue&nbsp;=&nbsp;yourMethod(param,&nbsp;out&nbsp;outParam));

lambda表达式类似于用匿名方法代替委托实例。

1
2
delegate int MyDelagate (int i);
MyDelagate delSquareFunction = x => x * x;

考虑lambda表达式x => x * x;

0

lambda表达式的代码可以是语句块而不是表达式。

1
x => {return x * x;};

例子

注:Func是一个预定义的通用委托。

1
2
3
4
5
6
    Console.WriteLine(MyMethod(x =>"Hi" + x));

    public static string MyMethod(Func<string, string> strategy)
    {
        return strategy("Lijo").ToString();
    }

工具书类

  • 如何可以交替使用代理和接口?

  • lambda表达式是表示匿名方法的简洁方法。匿名方法和lambda表达式都允许您以内联方式定义方法实现,但是匿名方法显式要求您定义方法的参数类型和返回类型。lambda表达式使用C 3.0的类型推断功能,该功能允许编译器根据上下文推断变量的类型。这是非常方便的,因为这节省了我们很多打字时间!


    很多时候,你只在一个地方使用功能,所以让一个方法把类搞得一团糟。


    这是一种采用小操作并将其非常接近使用位置的方法(与在其使用点附近声明变量不同)。这是为了使代码更可读。通过匿名化表达式,如果将函数用于其他地方并修改为"增强"它,那么就很难有人破坏您的客户机代码。

    同样,为什么需要使用foreach?您可以使用简单的for循环或直接使用IEnumerable在foreach中执行所有操作。答:您不需要它,但它使您的代码更具可读性。


    这也许是解释为什么要使用lambda表达式的最好方法->https://youtu.be/j9nj5dto54q

    总之,它的目的是提高代码的可读性,通过重用而不是复制代码来减少出错的可能性,并利用后台发生的优化。


    创新在于类型安全和透明。虽然您没有声明lambda表达式的类型,但它们是推断出来的,可以由代码搜索、静态分析、重构工具和运行时反射使用。

    例如,在您可能使用SQL并可能受到SQL注入攻击之前,因为黑客传递了一个字符串,通常需要一个数字。现在,您将使用一个Linq lambda表达式,它是受保护的。

    在纯委托上构建LinqAPI是不可能的,因为它需要在评估表达式树之前将它们组合在一起。

    2016年,大多数流行语言都支持lambda表达式,而c是主流命令式语言中这种进化的先驱之一。