C# 匿名方法与lambda表达式
有人能提供匿名方法和lambda表达式之间的简明区别吗?C# 匿名方法与lambda表达式,c#,lambda,delegates,anonymous-methods,C#,Lambda,Delegates,Anonymous Methods,有人能提供匿名方法和lambda表达式之间的简明区别吗? 匿名方法的使用: private void DoSomeWork() { if (textBox1.InvokeRequired) { //textBox1.Invoke((Action)(() => textBox1.Text = "test")); textBox1.Invoke((Action)delegate { textBox1.Text = "test"; });
匿名方法的使用:
private void DoSomeWork()
{
if (textBox1.InvokeRequired)
{
//textBox1.Invoke((Action)(() => textBox1.Text = "test"));
textBox1.Invoke((Action)delegate { textBox1.Text = "test"; });
}
}
它只是一个普通的lambda表达式被强制转换为强类型委托,还是有更多的lambda表达式被隐藏。我很清楚,像这样的强类型委托
UpdateTextDelegate mydelegate = new UpdateTextDelegate(MethodName)
作为
System.Delegate
类型的参数就足够了,但是匿名方法的概念对我来说是相当新的。准确地说,您所称的“匿名委托”实际上是一个匿名方法
嗯,lambdas和匿名方法都只是语法糖。编译器将至少为您生成一个“普通”方法,尽管有时(在闭包的情况下)它会生成一个嵌套类,其中不再包含匿名方法。什么是匿名方法?真的是匿名的吗?它有名字吗?所有的问题都很好,所以让我们先从它们开始,然后逐步学习lambda表达式
执行此操作时:
public void TestSomething()
{
Test(delegate { Debug.WriteLine("Test"); });
}
到底发生了什么
编译器首先决定获取方法的“主体”,即:
Debug.WriteLine("Test");
并将其分离成一个方法
编译器现在必须回答两个问题:
delegate{
部分回答了这个问题。该方法不接受任何参数(在delegate
和{
之间没有任何参数),并且由于我们不关心它的名称(因此称为“匿名”部分),因此我们可以将该方法声明为:
public void SomeOddMethod()
{
Debug.WriteLine("Test");
}
但它为什么要这么做
让我们看一下委托,例如Action
实际上是什么
如果我们暂时忽略.NET中的委托实际上是多个单个“委托”的链接列表这一事实,那么委托就是指向两个事物的引用(指针):
public void TestSomething()
{
Test(new Action(this.SomeOddMethod));
}
private void SomeOddMethod()
{
Debug.WriteLine("Test");
}
现在,问题是编译器无法知道Test
对给定的委托实际做了什么,因为委托的一半是对要调用方法的实例的引用,this
在上面的示例中,我们不知道将引用多少数据
例如,考虑上面的代码是否是一个巨大的对象的一部分,但只是一个临时生存的对象。也会把自己和那个巨大物体的生命联系起来,在很长一段时间内保持对它的引用,可能不太好
所以编译器不仅仅是创建一个方法,它还创建一个类来保存它。这回答了第一个问题,我应该把它放在哪里 因此,上述代码可以重写如下:public void TestSomething()
{
var temp = new SomeClass;
Test(new Action(temp.SomeOddMethod));
}
private class SomeClass
{
private void SomeOddMethod()
{
Debug.WriteLine("Test");
}
}
var customers =
db.Customers
.Where(customer => customer.Name == "ACME")
.Select(customer => customer.Address");
也就是说,在这个例子中,匿名方法的真正含义是什么
如果你开始使用局部变量,事情会变得更毛茸茸的,考虑这个例子:
public void Test()
{
int x = 10;
Test(delegate { Debug.WriteLine("x=" + x); });
}
这就是引擎盖下发生的事情,或者至少是非常接近它的事情:
public void TestSomething()
{
var temp = new SomeClass;
temp.x = 10;
Test(new Action(temp.SomeOddMethod));
}
private class SomeClass
{
public int x;
private void SomeOddMethod()
{
Debug.WriteLine("x=" + x);
}
}
编译器创建一个类,将该方法所需的所有变量提升到该类中,并将对局部变量的所有访问重写为对匿名类型上的字段的访问
类的名称和方法有点奇怪,让我们问问它是什么:
void Main()
{
int x = 10;
Test(delegate { Debug.WriteLine("x=" + x); });
}
public void Test(Action action)
{
action();
}
如果我要求LINQPad输出此程序的IL(中间语言),我会得到以下结果:
// var temp = new UserQuery+<>c__DisplayClass1();
IL_0000: newobj UserQuery+<>c__DisplayClass1..ctor
IL_0005: stloc.0 // CS$<>8__locals2
IL_0006: ldloc.0 // CS$<>8__locals2
// temp.x = 10;
IL_0007: ldc.i4.s 0A
IL_0009: stfld UserQuery+<>c__DisplayClass1.x
// var action = new Action(temp.<Main>b__0);
IL_000E: ldarg.0
IL_000F: ldloc.0 // CS$<>8__locals2
IL_0010: ldftn UserQuery+<>c__DisplayClass1.<Main>b__0
IL_0016: newobj System.Action..ctor
// Test(action);
IL_001B: call UserQuery.Test
Test:
IL_0000: ldarg.1
IL_0001: callvirt System.Action.Invoke
IL_0006: ret
<>c__DisplayClass1.<Main>b__0:
IL_0000: ldstr "x="
IL_0005: ldarg.0
IL_0006: ldfld UserQuery+<>c__DisplayClass1.x
IL_000B: box System.Int32
IL_0010: call System.String.Concat
IL_0015: call System.Diagnostics.Debug.WriteLine
IL_001A: ret
<>c__DisplayClass1..ctor:
IL_0000: ldarg.0
IL_0001: call System.Object..ctor
IL_0006: ret
在这种情况下也是一样的,这是生成匿名方法的快捷方式
您可以用两种方式来编写:
() => { ... code here ... }
() => ... single expression here ...
在第一种形式中,您可以编写在普通方法体中执行的所有代码。在第二种形式中,您可以编写一个表达式或语句
但是,在这种情况下,编译器将处理以下问题:
() => ...
与此相同:
delegate { ... }
它们仍然是匿名方法,只是()=>
语法是获取它的捷径
那么,如果这是通往它的捷径,我们为什么要拥有它
好吧,它使生活变得更容易,因为它被添加的目的,就是LINQ
考虑一下LINQ声明:
var customers = from customer in db.Customers
where customer.Name == "ACME"
select customer.Address;
该代码重写如下:
public void TestSomething()
{
var temp = new SomeClass;
Test(new Action(temp.SomeOddMethod));
}
private class SomeClass
{
private void SomeOddMethod()
{
Debug.WriteLine("Test");
}
}
var customers =
db.Customers
.Where(customer => customer.Name == "ACME")
.Select(customer => customer.Address");
如果要使用delegate{…}
语法,则必须使用return…
等重写表达式,它们看起来会更时髦。因此,添加了lambda语法,以使我们程序员在编写上述代码时的工作更轻松
那么什么是表达式呢?
到目前为止,我还没有说明如何定义Test
,但让我们为上述代码定义Test
:
public void Test(Action action)
这应该足够了,它说“我需要一个委托,它是Action类型的(不带参数,不返回值)”
但是,Microsoft还添加了一种不同的方法来定义此方法:
public void Test(Expression<Func<....>> expr)
不会实际传入委托,也不会(立即)传入任何可以调用的内容。相反,编译器会将此代码重写为与以下代码类似(但完全不同)的内容:
var operand1 = new VariableReferenceOperand("x");
var operand2 = new ConstantOperand(10);
var expression = new AdditionOperator(operand1, operand2);
Test(expression);
基本上,编译器将构建一个表达式
对象,其中包含对变量、文字值、所用运算符等的引用,并将该对象树传递给方法
为什么?
嗯,考虑<代码> DB。客户。(……)/代码>以上部分。
如果不是将所有客户(以及他们的所有数据)从数据库下载到客户机,循环遍历所有客户,找出哪个客户的名字正确等等,代码实际上会要求数据库立即找到一个正确的客户,这不是很好吗 这就是表达式背后的目的。实体框架、Linq2SQL或任何其他支持LINQ的数据库层都将获取该表达式,对其进行分析,然后选择一个表达式Test(() => Debug.WriteLine("Test"));
Customers.Where(delegate(Customers c) { return c.City == "London";});
Customers.Where(c => c.City == "London");
SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
FROM [Customers] AS [t0]
SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]
FROM [Customers] AS [t0]
WHERE [t0].[City] = @p0