为什么本地函数生成与匿名方法和Lambda表达式不同的IL?

Key*_*red 12 c# lambda cil anonymous-methods c#-7.0

为什么C#7编译器将本地函数转换为其父函数所在的同一类中的方法.对于匿名方法(和Lambda表达式),编译器为每个父函数生成一个嵌套类,它将包含所有的匿名方法作为实例方法?

例如,C#代码(匿名方法):

internal class AnonymousMethod_Example
{
    public void MyFunc(string[] args)
    {
        var x = 5;
        Action act = delegate ()
        {
            Console.WriteLine(x);
        };
        act();
    }
}
Run Code Online (Sandbox Code Playgroud)

将生成IL代码(匿名方法)类似于:

.class private auto ansi beforefieldinit AnonymousMethod_Example
{
    .class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass0_0'
    {
        .field public int32 x

        .method assembly hidebysig instance void '<MyFunc>b__0' () cil managed 
        {
            ...
            AnonymousMethod_Example/'<>c__DisplayClass0_0'::x
            call void [mscorlib]System.Console::WriteLine(int32)
            ...
        }
        ...
    }
...
Run Code Online (Sandbox Code Playgroud)

虽然这样,C#代码(Local Function):

internal class LocalFunction_Example
{
    public void MyFunc(string[] args)
    {
        var x = 5;
        void DoIt()
        {
            Console.WriteLine(x);
        };
        DoIt();
    }
}
Run Code Online (Sandbox Code Playgroud)

将生成IL代码(本地函数)类似于:

.class private auto ansi beforefieldinit LocalFunction_Example
{
    .class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass0_0' extends [mscorlib]System.ValueType
    {
        .field public int32 x
    }

    .method public hidebysig instance void MyFunc(string[] args) cil managed 
    {
        ...
        ldc.i4.5
        stfld int32 LocalFunction_Example/'<>c__DisplayClass1_0'::x
        ...
        call void LocalFunction_Example::'<MyFunc>g__DoIt1_0'(valuetype LocalFunction_Example/'<>c__DisplayClass1_0'&)
    }

    .method assembly hidebysig static void '<MyFunc>g__DoIt0_0'(valuetype LocalFunction_Example/'<>c__DisplayClass0_0'& '') cil managed 
    {
        ...
        LocalFunction_Example/'<>c__DisplayClass0_0'::x
        call void [mscorlib]System.Console::WriteLine(int32)
         ...
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,DoIt函数已在与其父函数相同的类中变为静态函数.封闭变量x也变成了嵌套的字段struct(不像class匿名方法示例中那样嵌套).

小智 11

存储在委托中的匿名方法可以被任何代码调用,甚至是用不同语言编写的代码,在C#7出现之前的几年编译,并且编译器生成的CIL需要对所有可能的用途有效.这意味着在您的情况下,在CIL级别,该方法必须不带参数.

本地方法只能由同一个C#项目调用(来自包含方法,更具体),因此编译该方法的相同编译器也将被处理以编译对它的所有调用.因此,不存在匿名方法的这种兼容性问题.产生相同效果的任何CIL都可以在这里工作,因此最有效的方法是有意义的.在这种情况下,编译器重写以允许使用值类型而不是引用类型可以防止不必要的分配.