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

正如您在下面的代码中看到的,我已经将 Action<>对象声明为变量。

有人能告诉我为什么这个 action 方法委托表现得像静态方法吗?

Why does it return true in the following code?

密码:

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


Console.WriteLine(actionMethod.Method.IsStatic);


Console.Read();
}

产出:

example output of sample

10089 次浏览

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

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);

这将为 withClosure输出 false,为 withoutClosure输出 true

When you use a lambda expression, the compiler creates a little class to contain your method, this would compile to something like the following (the actual implementation most likely varies slightly):

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>实例实际上指向这些生成的类上的方法。

The "action method" is static only as a side effect of the implementation. This is a case of an anonymous method with no captured variables. Since there are no captured variables, the method has no additional lifetime requirements beyond those for local variables in general. If it did reference other local variables, its lifetime extends to the lifetime of those other variables (see sec. L.1.7, 局部变量, and sec. N.15.5.1, 捕获外部变量, in the C# 5.0 specification).

请注意,C # 规范只谈到将匿名方法转换为“表达式树”,而不是“匿名类”。虽然表达式树可以表示为额外的 C # 类,例如,在 Microsoft 编译器中,这个实现是不需要的(正如 sec 所承认的那样。C # 5.0规范中的 M.5.3)。因此,匿名函数是否是静态的是未定义的。此外,第 K.6节对表达式树的细节保留了很多空间。

The method has no closures and also references a static method itself (Console.WriteLine), so I would expect it to be static. The method will declare an enclosing anonymous type for a closure, but in this instance it is not required.

代表缓存行为在罗斯林发生了变化。以前,如前所述,任何未捕获变量的 lambda 表达式都在调用站点被编译成 static方法。Roslyn 改变了这种行为。现在,任何捕获变量或不捕获变量的 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);
}
}
}

委托缓存行为在罗斯林的改变谈到了为什么要做这样的改变。

从 C # 6开始,这将始终默认为实例方法,而且永远不会是静态的(因此 actionMethod.Method.IsStatic将始终为 false)。

看这里: 为什么没有捕获的 lambda 从 C # 5中的静态变成了 C # 6中的实例方法?

这里是 CSC 和 Roslyn 编译器的静态 lambda 表达式计算的差异?