C#Lambda表达式:为什么要使用它们?

时间:2020-03-06 15:04:50  来源:igfitidea点击:

我已经快速阅读了Microsoft Lambda Expression文档。

但是,这种示例有助于我更好地理解:

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

不过,我仍然不明白为什么这是一项创新。这只是一种在"方法变量"结束时消失的方法,对吗?为什么要使用此方法而不是实际方法?

解决方案

很多时候,我们只在一个地方使用功能,因此制作方法只会使类混乱。

不必将方法定义在远离使用位置的地方,而不必在特定位置仅使用一次。很好的用途是用作排序等通用算法的比较器,然后我们可以在其中定义自定义排序函数,以在其中调用排序,而不是进一步强迫我们到其他地方查看正在排序的内容。

这并不是真正的创新。 LISP具有lambda函数已超过30年。

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

考虑以下示例:

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

相对

public string FindPerson(string nameContains, List<string> persons)
 {
     foreach (string person in persons)
         if (person.Contains(nameContains))
             return person;
     return null;
 }

这些在功能上是等效的。

这是一种进行小操作并将其非常靠近其使用位置的方式(与声明变量接近其使用点的方式不同)。这应该使代码更具可读性。通过使表达式匿名化,如果该函数在其他地方使用并被修改为"增强"功能,我们将使他人更难以破坏客户端代码。

同样,为什么需要使用foreach?我们可以使用普通的for循环或者仅直接使用IEnumerable在foreach中进行所有操作。答:我们不需要它,但是它使代码更具可读性。

Lambda表达式是表示匿名方法的一种简洁方法。匿名方法和Lambda表达式都允许我们内联定义方法实现,但是,匿名方法明确要求我们定义方法的参数类型和返回类型。 Lambda表达式使用C3.0的类型推断功能,该功能允许编译器根据上下文推断变量的类型。它非常方便,因为这样可以节省很多打字时间!

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

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");

strings.Find(s => s == "hello");

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

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);

编辑:

在C2.0中,可以使用匿名委托语法完成此操作:

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

Lambda大大清除了该语法。

Lambda表达式是匿名委托的一种较简单的语法,可以在可以使用匿名委托的任何地方使用。但是,事实并非如此。可以将lambda表达式转换为表达式树,这使LINQ to SQL有了很多魔力。

下面是一个使用匿名委托然后使用lambda表达式的LINQ to Objects表达式的示例,以显示它们在眼睛上有多容易:

// 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表达式和匿名委托比编写单独的函数有一个优势:它们实现闭包,可以使我们将局部状态传递给函数,而无需向函数添加参数或者创建一次性对象。

表达式树是C3.0的一项非常强大的新功能,它允许API查看表达式的结构,而不仅仅是获取对可以执行的方法的引用。 API只需将一个委托参数变成一个" Expression <T>"参数,编译器就会从一个lambda生成一个表达式树,而不是一个匿名委托:

void Example(Predicate<int> aDelegate);

像这样

Example(x => x > 5);

变成:

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

后者将传递一个抽象语法树的表示形式,该抽象语法树描述了表达式" x> 5"。 LINQ to SQL依靠此行为,能够将Cexpressions转换为服务器端进行过滤/排序等所需的SQL表达式。

Lambda清理了C2.0的匿名委托语法...例如

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

在C2.0中是这样完成的:

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

从功能上讲,它们执行完全相同的操作,只是语法更加简洁。

微软为我们提供了一种更清洁,更便捷的方式来创建名为Lambda表达式的匿名委托。但是,该语句的表达式部分没有引起太多关注。微软发布了整个命名空间System.Linq.Expressions,其中包含用于基于lambda表达式创建表达式树的类。表达式树由代表逻辑的对象组成。例如,x = y + z是一个表达式,它可能是.Net中表达式树的一部分。考虑以下(简单)示例:

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();
        }
    }
}

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

当我想使用另一个控件为某个控件的事件声明处理程序时,我发现它们很有用。
为此,通常必须将控件的引用存储在类的字段中,以便可以将其用于与创建控件不同的方法中。

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表达式,我们可以像这样使用它:

public CreateControls()
{
    ComboBox combo = new ComboBox();
    Label label = new Label();
    //some initializing code
    combo.SelectedIndexChanged += (s, e) => {label.Text = combo.SelectedValue;};
}

容易得多。