C#编译器是将lambda表达式视为公共方法还是私有方法?
在内部,编译器应该将lambda表达式转换为方法。在这种情况下,这些方法是私有的还是公共的(或其他什么),有可能改变吗?视情况而定。在当前版本的VisualStudio中,实现lambdas的方法从来都不是公共的,但它们并不总是私有的。测试lambdas某些版本的简单程序:C#编译器是将lambda表达式视为公共方法还是私有方法?,c#,compiler-optimization,C#,Compiler Optimization,在内部,编译器应该将lambda表达式转换为方法。在这种情况下,这些方法是私有的还是公共的(或其他什么),有可能改变吗?视情况而定。在当前版本的VisualStudio中,实现lambdas的方法从来都不是公共的,但它们并不总是私有的。测试lambdas某些版本的简单程序: public class Program { public static void Main() { var program = new Program(); Try("A",
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();
}
}
公共类程序
{
公共静态void Main()
{
var program=新程序();
Try(“A”,program.A);
Try(“B”,program.B);
Try(“C”,program.C);
Console.ReadKey();
}
私有静态void Try(字符串名称,Func生成器)
{
var mi=生成器()。方法;
WriteLine($“{name}:DeclaringType={mi.DeclaringType},Attributes={mi.Attributes}”);
}
私有操作A()=>()=>{};
私有操作B()=>()=>{ToString();};
私人行动C()
{
var c=1;
return()=>c.ToString();
}
}
印刷品
A:DeclaringType=Scratch.Program+c,Attributes=PrivateScope,Assembly,HideBySig
B:DeclaringType=Scratch.Program,Attributes=PrivateScope,Private,hidebysing
C:DeclaringType=Scratch.Program+C__DisplayClass4_0,属性=PrivateScope,Assembly,HideBySig
A
的lambda没有任何捕获。它被创建为空闭包类的内部方法
B
的lambda捕获此
。它被创建为包含类的private
方法
C
的lambda捕获C
。它被创建为非空闭包类的内部方法
所有这些都是未记录的,在过去已经发生了变化,因此最好避免依赖它。重要的是,当您调用匿名方法时,它的行为符合指定。如果你需要更多的东西,你不应该使用匿名方法。根据您的目标,您可能仍然可以使用lambdas,但需要使用表达式树,或者您可能需要创建常规命名方法。从CLR通过Jeffrey Richter的C#book
编译器会自动在类中定义一个新的私有方法
。。。编译器会自动为您创建方法的名称
。。。编译器生成的匿名方法总是以
是私有的,并且该方法是静态的或非静态的,具体取决于
方法是否访问任何实例成员
因此,该方法被声明为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'
编译器将对其进行优化,并且根本不会有lambda声明
IL_0001: ldstr "Hello World"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
正如@hvd所提到的,lambda表达式是否使用来自其周围环境(闭包情况)的参数是有区别的。见:
因此,只有当lambda表达式可以转换为委托包装器而不具有任何外部依赖项时,这个问题对于非闭包情况才有意义
您可以传递生成的类(基本上是包装一个委托),它将始终引用定义程序集中生成的委托。因此,如果引用了程序集,则可以从任何位置调用它
尽管Action.Method
本身被标记为internal,但刚刚验证了传递和执行在另一个程序集中定义的操作是有效的
// 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);
}
}
}
在内部,编译器应该将lambda表达式转换为方法
我假设“lambda”是指转换为委托类型的lambda。转换为表达式树类型的lambda肯定不会作为方法生成
事实上,编译器确实将这些lambda转换为方法,是的。没有要求它这样做,但这样做很方便
在这种情况下,这些方法是私有的还是公共的(或其他的),有可能改变吗
这个问题有点语无伦次。假设我告诉过你lambda是一种公共方法。它没有可从C#访问的名称;你会如何利用它的公共性?辅助功能修饰符应用于具有名称的成员。可访问性域的概念在名称解析过程中给出了名称的域
当然,在实践中,编译器必须为uncallable by you方法的元数据生成一些可访问性位。在闭包类上生成的方法是内部的,因为这是使它们的使用可验证的最方便的方法。不使用闭包生成的方法可以是私有的
同样,这一切都不是必需的,所有这些都是可能发生变化的实现细节。您不应该试图利用编译器的代码生成细节。afaik编译器将创建一个包含此lambda as方法的完整类。因此,要能够调用它,它至少应该是内部的
。但我不是编译器专家。@RenéVogt,这取决于lambda是否捕获了任何东西。如果没有,就不需要闭包类。如果它是公共的,你会怎么称呼它?它的名字只有编译器知道。除了包含类之外,它不被任何人使用,因此它没有任何理由是私有的。@AlexSk虽然可以回答您提出的问题,但我觉得您提出这个问题有更深层次的原因。如果你提供了更深层次的原因,那么你得到的答案实际上可能对你有用。只要创建一个单独的公共方法,如果你需要的话。从VS2015开始,这些信息不再准确。作为一种优化,新编译器避免创建静态方法,因为非静态方法在通过委托调用时提供了少量性能提升。@AlexanderDerck详细介绍了开发人员在Roslyn问题跟踪程序中提供的内容和原因
namespace OtherAssembly
{
public interface IB
{
void AddAction(Action act);
}
public class A
{
public void AddActionTo(IB b)
{
Action act = () => { };
b.AddAction(act);
}
}
}