46

Internally, the compiler should be translating lambda expressions to methods. In that case, would these methods be private or public (or something else) and is it possible to change that?

Alex Sk
  • 555
  • 4
  • 10
  • afaik the compiler creates a whole class containing this lambda as method. So to be able to call it, it should at least be `internal`. But I'm no compiler expert. – René Vogt Mar 07 '16 at 09:16
  • @RenéVogt That depends on whether the lambda captures anything. If it doesn't, there's no need for the closure class. –  Mar 07 '16 at 09:16
  • 3
    If it were public, how would you call it? It doesn't have a name known to anyone but the compiler. It isn't used by anyone but the containing class, so there's no reason for it to be anything but private. – Dennis_E Mar 07 '16 at 09:17
  • 15
    @AlexSk While it's possible to answer just the question you asked, I get the impression you have a deeper reason for asking this question. If you provide that deeper reason, the answers you get may actually be useful for you. –  Mar 07 '16 at 09:18
  • Just create a separate public method, if that's what you need. – Damien_The_Unbeliever Mar 07 '16 at 09:18
  • Which C# compiler? There are more than one... Or are you asking about language specification, rather than language implementation? – jub0bs Mar 07 '16 at 22:45

4 Answers4

57

It depends. With the current version of Visual Studio, the methods that implement lambdas are never public, but they're not always private. A simple program to test some versions of 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();
    }
}

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. It's created as an internal method of an empty closure class.

B's lambda captures this. It's created as a private method of the containing class.

C's lambda captures c. It's created as an internal method of a non-empty closure class.

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.

Kirill Rakhman
  • 42,195
  • 18
  • 124
  • 148
  • Nice answer. But, what does capturing has to do with the visibility of the method? What could have gone wrong if `A` was set to `private` (or `B` set to `internal`)? – haim770 Mar 07 '16 at 12:54
  • 5
    @haim770 The method containing the anonymous method needs to be able to access the method in order to construct the delegate. If the anonymous method is created as a member of the same class, it can be `private`, since methods can access private methods of their own class. If the anonymous method is created as a member of a different class, it needs to be at least `internal`, since methods cannot access private methods of other classes. Increasing visibility (making everything `internal`) would be possible, but there's no reason for making it more visible than needed. –  Mar 07 '16 at 13:02
  • Note that the generated closure class is private, so, from the outside, the method is *effectively private*. – svick Mar 07 '16 at 20:10
26

Internally, the compiler should be translating lambda expressions to methods.

I assume by "lambda" you mean a lambda converted to a delegate type. Lambdas converted to expression tree types are certainly not generated as methods.

The compiler does in fact turn such lambdas into methods, yes. 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. It has no name accessible from 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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
8

From the CLR via C# book by Jeffrey Richter

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.

For example the code

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

will produce IL declaration as

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

As you can see it is private static field.

However notice that lambda expression can be optimized, if you change example to

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

IL_0001:  ldstr      "Hello World"
IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
Nikolay K
  • 3,770
  • 3
  • 25
  • 37
2

As @hvd mentioned there's a difference between a lambda expression uses parameters from its surrounding environment (closure case) or not. See: Why do some C# lambda expressions compile to static methods?

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.

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.

// 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);
        }
    }
}
Community
  • 1
  • 1
Benji Wa
  • 147
  • 7