简体   繁体   English

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

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

Internally, the compiler should be translating lambda expressions to methods. 在内部,编译器应该将lambda表达式转换为方法。 In that case, would these methods be private or public (or something else) and is it possible to change that? 在这种情况下,这些方法是私有还是公共(或其他),是否可以改变它?

It depends. 这取决于。 With the current version of Visual Studio, the methods that implement lambdas are never public, but they're not always private. 使用当前版本的Visual Studio,实现lambda的方法永远不会公开,但它们并不总是私有的。 A simple program to test some versions of lambdas: 一个测试某些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();
    }
}

prints 版画

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 's lambda doesn't have any captures. A人的lambda没有任何捕获。 It's created as an internal method of an empty closure class. 它是作为空闭包类的internal方法创建的。

B 's lambda captures this . B的lambda抓住了this It's created as a private method of the containing class. 它是作为包含类的private方法创建的。

C 's lambda captures c . C的lambda捕获c It's created as an internal method of a non-empty closure class. 它是作为非空闭包类的internal方法创建的。

All of this is undocumented and has changed in the past, so it would be good to avoid relying on it. 所有这些都是无证的,并且在过去已经发生了变化,因此最好避免依赖它。 What matters is that when you call the anonymous method, it behaves as specified. 重要的是,当您调用匿名方法时,它的行为与指定的一样。 If you need anything more than that, you shouldn't be using anonymous methods. 如果您需要更多内容,则不应使用匿名方法。 Depending on what you're after, you might either still be able to use lambdas, but with expression trees, or you might need to create regular named methods instead. 根据您所使用的内容,您可能仍然可以使用lambdas,但使用表达式树,或者您可能需要创建常规命名方法。

Internally, the compiler should be translating lambda expressions to methods. 在内部,编译器应该将lambda表达式转换为方法。

I assume by "lambda" you mean a lambda converted to a delegate type. 我假设“lambda”是指lambda转换为委托类型。 Lambdas converted to expression tree types are certainly not generated as methods. 转换为表达式树类型的Lambdas肯定不会作为方法生成。

The compiler does in fact turn such lambdas into methods, yes. 事实上编译器确实将这样的lambda变成了方法,是的。 There is no requirement that it does so, but doing so is convenient. 没有要求它这样做,但这样做很方便。

In that case, would these methods be private or public (or something else) and is it possible to change that? 在这种情况下,这些方法是私有还是公共(或其他),是否可以改变它?

The question is somewhat incoherent. 这个问题有点不连贯。 Suppose I told you that a lambda was a public method. 假设我告诉你lambda是一种公共方法。 It has no name accessible from C#; 它没有可从C#访问的名称; how would you take advantage of its public-ness? 你会如何利用其公共利益? Accessibility modifiers apply to members with names. 辅助功能修饰符适用于具有名称的成员。 The very notion of accessibility domain gives the domain of a name during name resolution . 访问域的想法给出了一个名称名称解析过程中的域。

In practice of course the compiler has to generate some accessibility bits for the metadata of the uncallable-by-you method. 当然,在实践中,编译器必须为不可赎回方法的元数据生成一些可访问性位。 Methods generated on closure classes are internal, as that is the most convenient way to make usage of them verifiable. 在闭包类上生成的方法是内部的,因为这是使用它们可以验证的最方便的方法。 Methods generated without closures can be private. 没有闭包生成的方法可以是私有的。

Again, none of this is required, and all of it is implementation detail subject to change. 同样,这些都不是必需的,而且所有这些都是实施细节,可能会有所变化。 You should not be attempting to take advantage of the code generation details of the compiler. 您不应该尝试利用编译器的代码生成细节。

From the CLR via C# book by Jeffrey Richter 来自CLR,来自Jeffrey Richter的C#书

The compiler automatically defines a new private method in the class 编译器自动在类中定义一个新的私有方法

... The compiler creates the name of the method for you automatically ...编译器会自动为您创建方法的名称

... anonymous methods generated by the compiler always end up being private, and the method is either static or nonstatic depending on whether the method accesses any instance members ...编译器生成的匿名方法总是最终是私有的,并且该方法是静态的或非静态的,具体取决于该方法是否访问任何实例成员

So the method is declared as private or internal . 因此该方法被声明为privateinternal

For example the code 例如代码

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

will produce IL declaration as 将产生IL声明为

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

As you can see it is private static field. 正如您所看到的那样,它是private static字段。

However notice that lambda expression can be optimized, if you change example to 但是请注意,如果将示例更改为,则可以优化lambda表达式

class AClass
{
    string a = "Hello World";

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

compiler will optimize it and there would be no lambda declaration at all 编译器会优化它,根本就没有lambda声明

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

As @hvd mentioned there's a difference between a lambda expression uses parameters from its surrounding environment (closure case) or not. 正如@hvd提到的那样,lambda表达式使用周围环境中的参数(闭包大小写)与否之间存在差异。 See: Why do some C# lambda expressions compile to static methods? 请参阅: 为什么一些C#lambda表达式编译为静态方法?

So the question only makes sense for the non-closure case when the lambda expression can be converted into a delegate wrapper without having any outside dependencies. 因此,当lambda表达式可以转换为委托包装器而没有任何外部依赖关系时,这个问题只适用于非闭包情况。

You can pass that generated class (which basically wraps a delegate) around and it will always refer to the generated delegate in the defining assembly. 您可以传递生成的类(基本上包装一个委托),它将始终引用定义程序集中生成的委托。 So you can invoke it from anywhere if the assembly is referenced. 因此,如果引用程序集,您可以从任何位置调用它。

Just verified that passing and executing an Action defined in another assembly works although the Action.Method itself is marked internal. 刚刚验证了传递和执行在另一个程序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();
        }
    }
}

In other assembly: 在其他组装中:

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