Does the C# compiler treat a lambda expression as a public or private method?
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?
Solution 1:
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.
Solution 2:
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.
Solution 3:
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)
Solution 4:
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);
}
}
}