¿Por qué algunas expresiones lambda C # comstackn a métodos estáticos?

Como puede ver en el siguiente código, he declarado un objeto Action como una variable.

¿Alguien podría decirme por qué este delegado de método de acción se comporta como un método estático?

¿Por qué vuelve true en el siguiente código?

Código:

 public static void Main(string[] args) { Action actionMethod = s => { Console.WriteLine("My Name is " + s); }; Console.WriteLine(actionMethod.Method.IsStatic); Console.Read(); } 

Salida:

ejemplo de salida de la muestra

Esto es más probable porque no hay cierres, por ejemplo:

 int age = 25; Action withClosure = s => Console.WriteLine("My name is {0} and I am {1} years old", s, age); Action withoutClosure = s => Console.WriteLine("My name is {0}", s); Console.WriteLine(withClosure.Method.IsStatic); Console.WriteLine(withoutClosure.Method.IsStatic); 

Esto dará como resultado false para withClosure y true para withoutClosure .

Cuando utiliza una expresión lambda, el comstackdor crea una pequeña clase para contener su método, esto se comstackría de la siguiente manera (la implementación real probablemente varía ligeramente):

 private class 
b__0 { public int age; public void withClosure(string s) { Console.WriteLine("My name is {0} and I am {1} years old", s, age) } } private static class
b__1 { public static void withoutClosure(string s) { Console.WriteLine("My name is {0}", s) } } public static void Main() { var b__0 = new
b__0(); b__0.age = 25; Action withClosure = b__0.withClosure; Action withoutClosure =
b__1.withoutClosure; Console.WriteLine(withClosure.Method.IsStatic); Console.WriteLine(withoutClosure.Method.IsStatic); }

Puede ver que las instancias de Action resultantes apuntan a métodos en estas clases generadas.

El “método de acción” es estático solo como un efecto secundario de la implementación. Este es un caso de un método anónimo sin variables capturadas. Como no hay variables capturadas, el método no tiene requisitos de vida adicionales más allá de las variables locales en general. Si hizo referencia a otras variables locales, su duración se extiende a la vida útil de esas otras variables (consulte la sección L.1.7, Variables locales , y la sección N.15.5.1, Variables externas capturadas , en la especificación C # 5.0).

Tenga en cuenta que la especificación C # solo habla sobre métodos anónimos que se convierten en “árboles de expresión”, no en “clases anónimas”. Mientras que el árbol de expresiones podría representarse como clases adicionales de C #, por ejemplo, en el comstackdor de Microsoft, esta implementación no es necesaria (como lo reconoce la sección M.5.3 en la especificación de C # 5.0). Por lo tanto, no está definido si la función anónima es estática o no. Además, la sección K.6 deja mucho abierto en cuanto a los detalles de los árboles de expresión.

El comportamiento de almacenamiento en caché del delegado se modificó en Roslyn. Anteriormente, como se indicó, cualquier expresión lambda que no capturara variables se compiló en un método static en el sitio de llamadas. Roslyn cambió este comportamiento. Ahora, cualquier lambda, que captura variables o no, se transforma en una clase de visualización:

Dado este ejemplo:

 public class C { public void M() { var x = 5; Action action = y => Console.WriteLine(y); } } 

Salida del comstackdor nativo:

 public class C { [CompilerGenerated] private static Action CS$<>9__CachedAnonymousMethodDelegate1; public void M() { if (C.CS$<>9__CachedAnonymousMethodDelegate1 == null) { C.CS$<>9__CachedAnonymousMethodDelegate1 = new Action(C.b__0); } Action arg_1D_0 = C.CS$<>9__CachedAnonymousMethodDelegate1; } [CompilerGenerated] private static void b__0(int y) { Console.WriteLine(y); } } 

Roslyn:

 public class C { [CompilerGenerated] private sealed class <>c__DisplayClass0 { public static readonly C.<>c__DisplayClass0 CS$<>9__inst; public static Action CS$<>9__CachedAnonymousMethodDelegate2; static <>c__DisplayClass0() { // Note: this type is marked as 'beforefieldinit'. C.<>c__DisplayClass0.CS$<>9__inst = new C.<>c__DisplayClass0(); } internal void b__1(int y) { Console.WriteLine(y); } } public void M() { Action arg_22_0; if (arg_22_0 = C. <>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 == null) { C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 = new Action(C.<>c__DisplayClass0.CS$<>9__inst.b__1); } } } 

Delegar los cambios de comportamiento de caché en Roslyn habla sobre por qué se realizó este cambio.

El método no tiene cierres y también hace referencia a un método estático en sí mismo (Console.WriteLine), por lo que espero que sea estático. El método declarará un tipo anónimo cerrado para un cierre, pero en este caso no es obligatorio.

A partir de C # 6, esto siempre será el predeterminado para los métodos de instancia ahora, y nunca será estático (por lo que actionMethod.Method.IsStatic siempre será falso).

Vea aquí: ¿Por qué una lambda sin captura ha cambiado de una estática en C # 5 a un método de instancia en C # 6?

y aquí: ¿ Diferencia en la evaluación de expresiones lambda estáticas del comstackdor de CSC y Roslyn?