Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/318.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 匿名方法与lambda表达式_C#_Lambda_Delegates_Anonymous Methods - Fatal编程技术网

C# 匿名方法与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"; });

有人能提供匿名方法和lambda表达式之间的简明区别吗?

匿名方法的使用:

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