簡體   English   中英

為什么某些C#lambda表達式可編譯為靜態方法?

[英]Why do some C# lambda expressions compile to static methods?

正如您在下面的代碼中看到的那樣,我已將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節, 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中委托緩存行為更改,討論更改的原因。

該方法沒有閉包,也引用了靜態方法本身(Console.WriteLine),因此我希望它是靜態的。 該方法將為閉包聲明一個封閉的匿名類型,但是在這種情況下,它不是必需的。

從C#6開始,現在它將始終默認為實例方法,並且永遠不會是靜態的(因此actionMethod.Method.IsStatic將始終為false)。

請參見此處: 為什么沒有捕獲的lambda從C#5中的靜態更改為C#6中的實例方法?

此處: CSC和Roslyn編譯器的靜態lambda表達式評估的區別?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM