[英]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#书
编译器自动在类中定义一个新的私有方法
...编译器会自动为您创建方法的名称
...编译器生成的匿名方法总是最终是私有的,并且该方法是静态的或非静态的,具体取决于该方法是否访问任何实例成员
因此该方法被声明为private
或internal
。
例如代码
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.