簡體   English   中英

C#編譯器是否將lambda表達式視為公共或私有方法?

[英]Does the C# compiler treat a lambda expression as a public or private method?

在內部,編譯器應該將lambda表達式轉換為方法。 在這種情況下,這些方法是私有還是公共(或其他),是否可以改變它?

這取決於。 使用當前版本的Visual Studio,實現lambda的方法永遠不會公開,但它們並不總是私有的。 一個測試某些lambda版本的簡單程序:

public class Program
{
    public static void Main()
    {
        var program = new Program();
        Try("A", program.A);
        Try("B", program.B);
        Try("C", program.C);
        Console.ReadKey();
    }

    private static void Try(string name, Func<Action> generator)
    {
        var mi = generator().Method;
        Console.WriteLine($"{name}: DeclaringType={mi.DeclaringType}, Attributes={mi.Attributes}");
    }

    private Action A() => () => { };
    private Action B() => () => { ToString(); };
    private Action C()
    {
        var c = 1;
        return () => c.ToString();
    }
}

版畫

A: DeclaringType=Scratch.Program+<>c, Attributes=PrivateScope, Assembly, HideBySig
B: DeclaringType=Scratch.Program, Attributes=PrivateScope, Private, HideBySig
C: DeclaringType=Scratch.Program+<>c__DisplayClass4_0, Attributes=PrivateScope, Assembly, HideBySig

A人的lambda沒有任何捕獲。 它是作為空閉包類的internal方法創建的。

B的lambda抓住了this 它是作為包含類的private方法創建的。

C的lambda捕獲c 它是作為非空閉包類的internal方法創建的。

所有這些都是無證的,並且在過去已經發生了變化,因此最好避免依賴它。 重要的是,當您調用匿名方法時,它的行為與指定的一樣。 如果您需要更多內容,則不應使用匿名方法。 根據您所使用的內容,您可能仍然可以使用lambdas,但使用表達式樹,或者您可能需要創建常規命名方法。

在內部,編譯器應該將lambda表達式轉換為方法。

我假設“lambda”是指lambda轉換為委托類型。 轉換為表達式樹類型的Lambdas肯定不會作為方法生成。

事實上編譯器確實將這樣的lambda變成了方法,是的。 沒有要求它這樣做,但這樣做很方便。

在這種情況下,這些方法是私有還是公共(或其他),是否可以改變它?

這個問題有點不連貫。 假設我告訴你lambda是一種公共方法。 它沒有可從C#訪問的名稱; 你會如何利用其公共利益? 輔助功能修飾符適用於具有名稱的成員。 訪問域的想法給出了一個名稱名稱解析過程中的域。

當然,在實踐中,編譯器必須為不可贖回方法的元數據生成一些可訪問性位。 在閉包類上生成的方法是內部的,因為這是使用它們可以驗證的最方便的方法。 沒有閉包生成的方法可以是私有的。

同樣,這些都不是必需的,而且所有這些都是實施細節,可能會有所變化。 您不應該嘗試利用編譯器的代碼生成細節。

來自CLR,來自Jeffrey Richter的C#書

編譯器自動在類中定義一個新的私有方法

...編譯器會自動為您創建方法的名稱

...編譯器生成的匿名方法總是最終是私有的,並且該方法是靜態的或非靜態的,具體取決於該方法是否訪問任何實例成員

因此該方法被聲明為privateinternal

例如代碼

class AClass {
    public void SomeMethod() {
        Action lambda = () => Console.WriteLine("Hello World");
        lambda();
    }
}

將產生IL聲明為

.field private static class [mscorlib]System.Action 'CS$<>9__CachedAnonymousMethodDelegate1'

正如您所看到的那樣,它是private static字段。

但是請注意,如果將示例更改為,則可以優化lambda表達式

class AClass
{
    string a = "Hello World";

    public void SomeMethod()
    {
        Action lambda = () => Console.WriteLine(a);
        lambda();
    }
}

編譯器會優化它,根本就沒有lambda聲明

IL_0001:  ldstr      "Hello World"
IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)

正如@hvd提到的那樣,lambda表達式使用周圍環境中的參數(閉包大小寫)與否之間存在差異。 請參閱: 為什么一些C#lambda表達式編譯為靜態方法?

因此,當lambda表達式可以轉換為委托包裝器而沒有任何外部依賴關系時,這個問題只適用於非閉包情況。

您可以傳遞生成的類(基本上包裝一個委托),它將始終引用定義程序集中生成的委托。 因此,如果引用程序集,您可以從任何位置調用它。

剛剛驗證了傳遞和執行在另一個程序Action.Method定義的Action是有效的,盡管Action.Method本身標記為內部。

// Main, first assembly
namespace ConsoleApplication1
{
    public class B : IB
    {
        Action _action;
        public void AddAction(Action act)
        {
            _action = act;
        }

        public void Invoke()
        {
            Console.WriteLine(_action.Target);
            Console.WriteLine("Is public: {0}", _action.Method.IsPublic);
            _action();
        }

    }

    class Program
    {
        static void Main(string[] args)
        {
            var a = new A();
            var b = new B();
            a.AddActionTo(b);
            b.Invoke();

            Console.ReadKey();
        }
    }
}

在其他組裝中:

namespace OtherAssembly
{
    public interface IB
    {
        void AddAction(Action act);
    }

    public class A
    {
        public void AddActionTo(IB b)
        {
            Action act = () => { };
            b.AddAction(act);
        }
    }
}

暫無
暫無

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

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