Answers:
这很可能是因为没有闭包,例如:
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);
这将输出false
的withClosure
和true
为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);
}
您可以看到生成的Action<string>
实例实际上指向这些生成的类上的方法。
Ildasm
对于了解实际情况非常有用,我倾向于使用的IL
选项卡LINQPad
检查小样本。
“动作方法”仅作为实现的副作用是静态的。这是没有捕获变量的匿名方法的情况。由于没有捕获的变量,因此该方法除了一般的局部变量之外,没有其他寿命要求。如果它确实引用了其他局部变量,则其生存期将扩展到那些其他变量的生存期(请参阅C#5.0规范中的L.1.7节和Local 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中委托缓存行为更改,讨论更改的原因。
从C#6开始,现在它将始终默认为实例方法,并且永远不会是静态的(因此actionMethod.Method.IsStatic
始终为false)。
static
方法的理想选择。