C#编译器是否将lambdaexpression式视为公共或私有方法?

在内部,编译器应该将lambdaexpression式转换为方法。 在这种情况下,这些方法是私有的还是公共的(或其他),是否有可能改变这种情况?

这取决于。 使用Visual Studio的当前版本,实现lambdaexpression式的方法从不公开,但并不总是私有的。 一个简单的程序来testing一些版本的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方法创build的。

B的lambda捕获this 。 它是作为包含类的private方法创build的。

C的lambda捕获c 。 它是作为非空的闭包类的internal方法创build的。

所有这些都是无证的,过去有所改变,所以最好避免依赖它。 重要的是当你调用匿名方法时,它的行为就像指定的那样。 如果你还需要更多的东西,你不应该使用匿名方法。 取决于你之后,你可能仍然可以使用lambdaexpression式,但是使用expression式树,或者你可能需要创build正规的命名方法。

在内部,编译器应该将lambdaexpression式转换为方法。

我认为“lambda”是指将lambda转换为委托types。 转换为expression式树的types肯定不是作为方法生成的。

编译器确实将这种lambdaexpression式转换为方法,是的。 没有要求它这样做,但这样做是方便的。

在这种情况下,这些方法是私有的还是公共的(或其他),是否有可能改变这种情况?

这个问题有些不一致。 假设我告诉过你,lambda是一种公开的方法。 它没有从C#访问的名字; 你将如何利用其公共性? 辅助function修饰符适用于具有名称的成员。 可访问性域的概念在名称parsing过程中给出了域名。

实际上,编译器必须为不可调用的方法的元数据生成一些可访问性位。 在闭包类上生成的方法是内部的,因为这是使它们可以被validation的最方便的方法。 没有closures生成的方法可以是私有的。

再一次,这些都不是必需的,所有这些都是实现细节可能会改变。 您不应该试图利用编译器的代码生成细节。

从CLR通过Jeffrey Richter的C#书

编译器自动在类中定义一个新的私有方法

…编译器自动为您创build方法的名称

…由编译器生成的匿名方法总是最终成为私有的,并且该方法是静态的还是非静态的,这取决于该方法是否访问任何实例成员

所以这个方法被声明为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字段。

不过请注意,如果将示例更改为,lambdaexpression式可以进行优化

 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提到的,lambdaexpression式使用来自周围环境的参数(闭包情况)与否有区别。 请参阅: 为什么一些C#lambdaexpression式编译为静态方法?

所以这个问题只对非closures情况有意义,当lambdaexpression式可以被转换成一个委托包装没有任何外部的依赖。

您可以传递生成的类(基本上是包装一个委托),它将始终引用定义程序集中生成的委托。 所以如果程序集被引用,你可以从任何地方调用它。

只是validation传递和执行另一个程序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); } } }