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

nun*_*unu 121 .net c# reflection 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();
}
Run Code Online (Sandbox Code Playgroud)

输出:

示例输出示例

Luk*_*oid 152

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

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);
Run Code Online (Sandbox Code Playgroud)

这将输出falsefor withClosuretruefor withoutClosure.

当您使用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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • @nunu在这个例子中,我使用了[`LINQPad`]的`IL`选项卡(http://www.linqpad.net/)并推断了C#.获得编译输出的实际C#等价物的一些选项是使用[`ILSpy`](http://ilspy.net/)或[`Reflector`](http://www.red-gate.com/)在编译的程序集上的products/dotnet-development/reflector /),你很可能需要禁用一些将尝试显示lambdas而不是编译器生成的类的选项. (8认同)
  • +1.可以确认 - 没有关闭它们是"静态"方法的完美候选者. (4认同)
  • @Liath`Ildasm`对于理解实际发生的事情非常有用,我倾向于使用`LINQPad`的`IL`选项卡来检查小样本. (4认同)
  • 我只是建议这个问题需要一些扩展,我回来了,就在那里.非常有用 - 很高兴看到编译器在幕后做了什么. (3认同)

Pet*_* O. 20

"动作方法"仅作为实现的副作用而是静态的.这是一个没有捕获变量的匿名方法的情况.由于没有捕获的变量,因此该方法除了局部变量之外没有额外的生命周期要求.如果它确实引用了其他局部变量,则其生命周期延伸到其他变量的生命周期(参见章节L.1.7,局部变量和秒N.15.5.1,捕获的外部变量,在C#5.0规范中).

请注意,C#规范仅讨论转换为"表达式树"的匿名方法,而不是"匿名类".虽然表达式树可以表示为其他C#类,例如,在Microsoft编译器中,不需要此实现(如C#5.0规范中的sec.M.5.3所承认).因此,未定义匿名函数是否为静态函数.此外,K.6节对表达树的细节留下了很多空白.

  • +1出于上述原因,最有可能不依赖于此行为; 这是一个非常实用的细节. (2认同)

Yuv*_*kov 18

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

鉴于这个例子:

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

本机编译器输出:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

罗斯林:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Roslyn中的代理缓存行为更改说明了为何进行此更改.

  • 谢谢,我想知道为什么我的Func <int> f =()=> 5的方法不是静态的 (2认同)