为什么一些C#lambda表达式编译为静态方法?

正如你在下面的代码中看到的那样,我已经声明了一个Action<>对象作为变量。

有人请让我知道为什么这个动作方法委托行为像一个静态方法?

为什么它在下面的代码中返回true

码:

public static void Main(string[] args)
{
    Action<string> actionMethod = s => { Console.WriteLine("My Name is " + s); };

    Console.WriteLine(actionMethod.Method.IsStatic);

    Console.Read();
}

输出:

示例输出示例


这很可能是因为没有关闭,例如:

int age = 25;
Action<string> withClosure = s => Console.WriteLine("My name is {0} and I am {1} years old", s, age);
Action<string> withoutClosure = s => Console.WriteLine("My name is {0}", s);
Console.WriteLine(withClosure.Method.IsStatic);
Console.WriteLine(withoutClosure.Method.IsStatic);

这将输出falsewithClosuretruewithoutClosure

当你使用lambda表达式时,编译器会创建一个包含你的方法的小类,这会编译成如下所示的内容(实际的实现很可能略有不同):

private class <Main>b__0
{
    public int age;
    public void withClosure(string s)
    {
        Console.WriteLine("My name is {0} and I am {1} years old", s, age)
    }
}

private static class <Main>b__1
{
    public static void withoutClosure(string s)
    {
        Console.WriteLine("My name is {0}", s)
    }
}

public static void Main()
{
    var b__0 = new <Main>b__0();
    b__0.age = 25;
    Action<string> withClosure = b__0.withClosure;
    Action<string> withoutClosure = <Main>b__1.withoutClosure;
    Console.WriteLine(withClosure.Method.IsStatic);
    Console.WriteLine(withoutClosure.Method.IsStatic);
}

您可以看到生成的Action<string>实例实际上指向这些生成的类上的方法。


“行动方法”仅作为实施的副作用而是静态的。 这是一个没有捕获变量的匿名方法。 由于没有捕获的变量,因此该方法不需要超出局部变量的额外生命期要求。 如果它引用了其他局部变量,则它的生命周期延伸到其他变量的生命周期(参见C#5.0规范中的第L.1.7节“局部变量”和第N.15.5.1节“捕获的外部变量”)。

请注意,C#规范仅讨论将匿名方法转换为“表达式树”,而不是“匿名类”。 虽然表达式树可以表示为额外的C#类,例如,在Microsoft编译器中,此实现不是必需的(如C#5.0规范中第M.5.3节所确认的)。 因此,不确定匿名函数是否为静态函数。 此外,关于表达树的细节,第K.6节还有很大的空缺。


委托缓存行为在Roslyn中进行了更改。 如前所述,任何未捕获变量的lambda表达式在调用站点都被编译为static方法。 罗斯林改变了这种行为。 现在,任何可以捕获变量的lambda都会被转换为一个显示类:

给出这个例子:

public class C
{
    public void M()
    {
        var x = 5;
        Action<int> action = y => Console.WriteLine(y);
    }
}

本地编译器输出:

public class C
{
    [CompilerGenerated]
    private static Action<int> CS$<>9__CachedAnonymousMethodDelegate1;
    public void M()
    {
        if (C.CS$<>9__CachedAnonymousMethodDelegate1 == null)
        {
            C.CS$<>9__CachedAnonymousMethodDelegate1 = new Action<int>(C.<M>b__0);
        }
        Action<int> arg_1D_0 = C.CS$<>9__CachedAnonymousMethodDelegate1;
    }
    [CompilerGenerated]
    private static void <M>b__0(int y)
    {
        Console.WriteLine(y);
    }
}

罗斯林:

public class C
{
    [CompilerGenerated]
    private sealed class <>c__DisplayClass0
    {
        public static readonly C.<>c__DisplayClass0 CS$<>9__inst;
        public static Action<int> CS$<>9__CachedAnonymousMethodDelegate2;
        static <>c__DisplayClass0()
        {
            // Note: this type is marked as 'beforefieldinit'.
            C.<>c__DisplayClass0.CS$<>9__inst = new C.<>c__DisplayClass0();
        }
        internal void <M>b__1(int y)
        {
            Console.WriteLine(y);
        }
    }
    public void M()
    {
        Action<int> arg_22_0;
        if (arg_22_0 = C.
                       <>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 == null)
        {
            C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 =
          new Action<int>(C.<>c__DisplayClass0.CS$<>9__inst.<M>b__1);
        }
    }
}

Roslyn中的委托缓存行为更改讨论了为什么会进行此更改。

链接地址: http://www.djcxy.com/p/90347.html

上一篇: Why do some C# lambda expressions compile to static methods?

下一篇: Defining a lambda expression with an anonymous type contained within that lambda