我已经快速阅读了 Microsoft Lambda Expression 文档。
但是,这种示例帮助我更好地理解:
delegate int del(int i);
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25
不过,我不明白为什么它是这样的创新。这只是“方法变量”结束时死亡的方法,对吗?为什么我要使用这个而不是真正的方法?
delegate
是什么的人,我强烈建议您在阅读此页面的其余部分之前阅读此内容:stackoverflow.com/questions/2082615/…
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 > 5
的 abstract syntax tree 表示。 LINQ to SQL 依靠这种行为能够将 C# 表达式转换为服务器端过滤/排序等所需的 SQL 表达式。
匿名函数和表达式对于无法从创建完整方法所需的额外工作中受益的一次性方法很有用。
考虑这个例子:
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;
}
这些在功能上是等效的。
当我想使用另一个控件为某个控件的事件声明一个处理程序时,我发现它们很有用。要正常执行此操作,您必须将控件的引用存储在类的字段中,以便您可以在与创建它们不同的方法中使用它们。
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;};
}
容易得多。
Lambda 清理了 C# 2.0 的匿名委托语法……例如
Strings.Find(s => s == "hello");
在 C# 2.0 中是这样完成的:
Strings.Find(delegate(String s) { return s == "hello"; });
从功能上讲,它们做的事情完全相同,只是语法更简洁。
这只是使用 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 显着清理了该语法。
string
并返回一个 bool
)作为 Find
方法本身的参数。
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 代码。现在我会使用表达式树。
它避免了必须在远离它们使用的地方定义的特定地方只使用一次的方法。良好的用途是作为通用算法(例如排序)的比较器,然后您可以在其中定义自定义排序函数,在其中调用排序而不是更远地迫使您查看其他地方以查看您正在排序的内容。
这并不是真正的创新。 LISP 已经拥有 lambda 函数大约 30 年或更长时间了。
您还可以在编写泛型代码以作用于您的方法时发现使用 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));
Lambda 表达式是一种表示匿名方法的简洁方式。匿名方法和 Lambda 表达式都允许您内联定义方法实现,但是,匿名方法明确要求您定义方法的参数类型和返回类型。 Lambda 表达式使用 C# 3.0 的类型推断功能,它允许编译器根据上下文推断变量的类型。这非常方便,因为这样可以节省我们大量的打字时间!
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();
}
参考
委托和接口如何互换使用?
很多时候,您只在一个地方使用该功能,因此创建一个方法只会使类变得混乱。
这是一种进行小操作并将其放置在非常靠近使用位置的方式(与声明靠近其使用点的变量不同)。这应该使您的代码更具可读性。通过匿名化表达式,如果该函数在其他地方使用并被修改以“增强”它,那么您也使某人更难破坏您的客户端代码。
同样,为什么需要使用foreach?您可以在 foreach 中使用普通的 for 循环或直接使用 IEnumerable 来完成所有操作。答:您不需要它,但它使您的代码更具可读性。
创新在于类型安全性和透明度。尽管您没有声明 lambda 表达式的类型,但它们是推断出来的,并且可以被代码搜索、静态分析、重构工具和运行时反射使用。
例如,在您可能使用过 SQL 并可能受到 SQL 注入攻击之前,因为黑客在通常期望数字的地方传递了一个字符串。现在您将使用 LINQ lambda 表达式,该表达式受到保护。
不可能在纯委托上构建 LINQ API,因为它需要在评估它们之前将表达式树组合在一起。
2016 年,大多数流行语言都支持 lambda expression,而 C# 是主流命令式语言中这一演变的先驱之一。
这也许是关于为什么使用 lambda 表达式的最佳解释 -> https://youtu.be/j9nj5dTo54Q
总之,它是为了提高代码的可读性,通过重用而不是复制代码来减少出错的机会,并利用幕后发生的优化。
lambda 表达式和匿名函数的最大好处是它们允许库/框架的客户端(程序员)通过给定库/框架中的代码注入功能(因为它是 LINQ、ASP.NET Core 和许多其他人)以常规方法无法做到的方式。但是,对于单个应用程序程序员来说,它们的优势并不明显,但对于创建库的人来说,这些库以后将被其他想要配置库代码行为的人或使用库的人使用。因此,有效使用 lambda 表达式的上下文是库/框架的使用/创建。
此外,由于它们描述了一次性使用代码,因此它们不必成为会导致更多代码复杂性的类的成员。想象一下,每次我们想要配置类对象的操作时,都必须声明一个焦点不明确的类。