metodo - ¿El compilador de C#trata una expresión lambda como un método público o privado?
metodo anonimo c# (4)
Internamente, el compilador debería traducir las expresiones lambda a métodos. En ese caso, ¿serían estos métodos privados o públicos (o algo más) y es posible cambiar eso?
Internamente, el compilador debería traducir las expresiones lambda a métodos.
Supongo que por "lambda" quiere decir una lambda convertida en un tipo de delegado. Las Lambdas convertidas a tipos de árbol de expresión ciertamente no se generan como métodos.
De hecho, el compilador convierte tales lambdas en métodos, sí. No hay ningún requisito de que lo haga, pero hacerlo es conveniente.
En ese caso, ¿serían estos métodos privados o públicos (o algo más) y es posible cambiar eso?
La pregunta es algo incoherente. Supongamos que te dijera que una lambda era un método público. No tiene nombre accesible desde C #; ¿Cómo aprovecharías su carácter público? Los modificadores de accesibilidad se aplican a los miembros con nombres. La noción misma de dominio de accesibilidad le da al dominio un nombre durante la resolución de nombres .
En la práctica, por supuesto, el compilador tiene que generar algunos bits de accesibilidad para los metadatos del método no recuperable por usted. Los métodos generados en las clases de cierre son internos, ya que es la forma más conveniente de hacerlos verificables. Los métodos generados sin cierres pueden ser privados.
Nuevamente, nada de esto es necesario, y todo esto es un detalle de implementación sujeto a cambios. No debe intentar aprovechar los detalles de generación de código del compilador.
Como se mencionó en @hvd, hay una diferencia entre una expresión lambda que usa parámetros de su entorno (caso de cierre) o no. Consulte: ¿Por qué algunas expresiones lambda de C # se compilan en métodos estáticos?
Por lo tanto, la pregunta solo tiene sentido para el caso de no cierre cuando la expresión lambda se puede convertir en un contenedor delegado sin tener dependencias externas.
Puede pasar esa clase generada (que básicamente envuelve a un delegado) y siempre se referirá al delegado generado en el ensamblaje que lo define. Por lo tanto, puede invocarlo desde cualquier lugar si se hace referencia al ensamblaje.
Solo se verificó que el paso y la ejecución de una Acción definida en otro conjunto funciona, aunque el propio Action.Method
está marcado como interno.
// 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();
}
}
}
En otra asamblea:
namespace OtherAssembly
{
public interface IB
{
void AddAction(Action act);
}
public class A
{
public void AddActionTo(IB b)
{
Action act = () => { };
b.AddAction(act);
}
}
}
Del libro CLR a través de C # por Jeffrey Richter
El compilador define automáticamente un nuevo método privado en la clase.
... El compilador crea el nombre del método automáticamente
... los métodos anónimos generados por el compilador siempre terminan siendo privados, y el método es estático o no estático dependiendo de si el método accede a algún miembro de la instancia
Así el método se declara como private
o internal
.
Por ejemplo el codigo
class AClass {
public void SomeMethod() {
Action lambda = () => Console.WriteLine("Hello World");
lambda();
}
}
producirá la declaración de IL como
.field private static class [mscorlib]System.Action ''CS$<>9__CachedAnonymousMethodDelegate1''
Como puedes ver es private static
campo private static
.
Sin embargo, tenga en cuenta que la expresión lambda se puede optimizar, si cambia de ejemplo a
class AClass
{
string a = "Hello World";
public void SomeMethod()
{
Action lambda = () => Console.WriteLine(a);
lambda();
}
}
el compilador lo optimizará y no habría ninguna declaración lambda en absoluto
IL_0001: ldstr "Hello World"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
Depende. Con la versión actual de Visual Studio, los métodos que implementan lambdas nunca son públicos, pero no siempre son privados. Un programa simple para probar algunas versiones de 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();
}
}
huellas dactilares
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
lambda de A no tiene ninguna captura. Se crea como un método internal
de una clase de cierre vacío.
La lambda de B
captura this
. Se crea como un método private
de la clase contenedora.
La lambda de c
captura. Se crea como un método internal
de una clase de cierre no vacío.
Todo esto no está documentado y ha cambiado en el pasado, por lo que sería bueno evitar confiar en él. Lo que importa es que cuando se llama al método anónimo, se comporta como se especifica. Si necesita algo más que eso, no debe usar métodos anónimos. Dependiendo de lo que esté buscando, es posible que aún pueda usar lambdas, pero con árboles de expresiones, o tal vez deba crear métodos con nombres regulares.