C#编译器是将lambda表达式视为公共方法还是私有方法?

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",

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

视情况而定。在当前版本的VisualStudio中,实现lambdas的方法从来都不是公共的,但它们并不总是私有的。测试lambdas某些版本的简单程序:

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);
        }
    }
}