ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

不过,我不明白为什么它是这样的创新。这只是“方法变量”结束时死亡的方法,对吗?为什么我要使用这个而不是真正的方法?

对于那些来到此页面但不知道 C# 中的 delegate 是什么的人,我强烈建议您在阅读此页面的其余部分之前阅读此内容:stackoverflow.com/questions/2082615/…
一个老问题.. 但本教程是对委托和 lambda 表达式的精彩解释,并带有演练代码。 youtu.be/R8Blt5c-Vi4

T
Ted Johnson

Lambda expressions 是一种更简单的匿名委托语法,可以在可以使用匿名委托的任何地方使用。然而,事实并非如此。 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 表达式和匿名委托有一个优势:它们实现了 closures,这可以让您对函数进行 pass local state to the function without adding parameters 或创建一次性使用的对象。

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

void Example(Predicate<int> aDelegate);

称为:

Example(x => x > 5);

变成:

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

后者将获得描述表达式 x > 5abstract syntax tree 表示。 LINQ to SQL 依靠这种行为能够将 C# 表达式转换为服务器端过滤/排序等所需的 SQL 表达式。


如果没有闭包,您可以使用静态方法作为回调,但您仍然必须在某个类中定义这些方法,几乎可以肯定地将此类方法的范围扩大到超出预期用途。
FWIW,您可以使用匿名委托进行闭包,因此您并不严格需要 lambdas。 Lambda 比匿名委托更易读,否则使用 Linq 会让你眼花缭乱。
a
amitklein

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

考虑这个例子:

 List<string> people = new List<string> { "name1", "name2", "joe", "another name", "etc" };
 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;
 }

这些在功能上是等效的。


将如何定义 Find() 方法来处理这个 lambda 表达式?
Predicate 是 Find 方法所期望的。
由于我的 lambda 表达式与 Predicate 的协定相匹配,因此 Find() 方法接受它。
您的意思是“字符串 person = people.Find(persons => persons.Contains("Joe"));”
@FKCoder,不,他没有,尽管如果他说“string person = people.Find(p => p.Contains("Joe"));”可能会更清楚
k
kmote

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

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

容易得多。


在第一个示例中,为什么不转换 sender 并获取值?
@Andrew:在这个简单的示例中,没有必要使用发送者,因为只有一个组件有问题,并且直接使用该字段可以节省强制转换,从而提高了清晰度。在现实世界的场景中,我个人也更喜欢使用发件人。通常,如果可能的话,我对多个事件使用一个事件处理程序,因此我必须识别实际的发送者。
B
BenMorel

Lambda 清理了 C# 2.0 的匿名委托语法……例如

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

在 C# 2.0 中是这样完成的:

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

从功能上讲,它们做的事情完全相同,只是语法更简洁。


它们并不完全相同 - 正如@Neil Williams 指出的那样,您可以使用表达式树提取 lambda 的 AST,而匿名方法不能以相同的方式使用。
这是 lambda 的许多其他优点之一。与匿名方法相比,它有助于更好地理解代码。当然,这不是创建 lambdas 的意图,但这些是可以更频繁地使用的场景。
F
FlySwat

这只是使用 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);

编辑:

在 C# 2.0 中,这可以使用匿名委托语法来完成:

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

Lambda 显着清理了该语法。


@Jonathan Holland:感谢您的编辑和添加匿名委托语法。它很好地完成了这个例子。
什么是匿名委托?//对不起,我是 C# 新手
@HackerMan,将匿名委托视为没有“名称”的函数。您仍在定义一个可以具有输入和输出的函数,但是由于它是一个名称,因此您不能直接引用它。在上面显示的代码中,您定义了一个方法(它接受一个 string 并返回一个 bool)作为 Find 方法本身的参数。
T
Thronk

Microsoft 为我们提供了一种更简洁、更方便的方式来创建称为 Lambda 表达式的匿名委托。但是,该语句的 表达式 部分并没有引起太多关注。 Microsoft 发布了一个完整的命名空间 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();
        }
    }
}

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


w
workmad3

它避免了必须在远离它们使用的地方定义的特定地方只使用一次的方法。良好的用途是作为通用算法(例如排序)的比较器,然后您可以在其中定义自定义排序函数,在其中调用排序而不是更远地迫使您查看其他地方以查看您正在排序的内容。

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


G
Gunasekaran

您还可以在编写泛型代码以作用于您的方法时发现使用 lambda 表达式。

例如:计算方法调用所用时间的通用函数。 (即此处的 Action

public static long Measure(Action action)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    action();
    sw.Stop();
    return sw.ElapsedMilliseconds;
}

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

var timeTaken = Measure(() => yourMethod(param));

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

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

V
Vijesh VP

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


C
Community

lambda 表达式就像是代替委托实例编写的匿名方法。

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

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

入参值为x(=>左侧) 函数逻辑为x * x(=>右侧)

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

x => {return x * x;};

例子

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

    Console.WriteLine(MyMethod(x => "Hi " + x));

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

参考

委托和接口如何互换使用?


D
Darren Kopp

很多时候,您只在一个地方使用该功能,因此创建一个方法只会使类变得混乱。


p
plinth

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

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


b
battlmonstr

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

例如,在您可能使用过 SQL 并可能受到 SQL 注入攻击之前,因为黑客在通常期望数字的地方传递了一个字符串。现在您将使用 LINQ lambda 表达式,该表达式受到保护。

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

2016 年,大多数流行语言都支持 lambda expression,而 C# 是主流命令式语言中这一演变的先驱之一。


c
coffeeeee

这也许是关于为什么使用 lambda 表达式的最佳解释 -> https://youtu.be/j9nj5dTo54Q

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


G
Grigoris Dimitroulakos

lambda 表达式和匿名函数的最大好处是它们允许库/框架的客户端(程序员)通过给定库/框架中的代码注入功能(因为它是 LINQ、ASP.NET Core 和许多其他人)以常规方法无法做到的方式。但是,对于单个应用程序程序员来说,它们的优势并不明显,但对于创建库的人来说,这些库以后将被其他想要配置库代码行为的人或使用库的人使用。因此,有效使用 lambda 表达式的上下文是库/框架的使用/创建。

此外,由于它们描述了一次性使用代码,因此它们不必成为会导致更多代码复杂性的类的成员。想象一下,每次我们想要配置类对象的操作时,都必须声明一个焦点不明确的类。