c# .net compiler-construction roslyn c#-6.0

c# - Delegar cambios de comportamiento de almacenamiento en caché en Roslyn



.net compiler-construction (2)

Dado el siguiente código:

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

Usando VS2013, .NET 4.5. Al mirar el código descompilado, podemos ver que el compilador está almacenando en caché al delegado en el sitio de la llamada:

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

Al mirar el mismo código descompilado en Roslyn (usando TryRoslyn ), se obtiene el siguiente resultado:

public class C { [CompilerGenerated] private sealed class <>c__DisplayClass0 { public static readonly C.<>c__DisplayClass0 CS$<>9__inst; public static Action<int> 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 <M>b__1(int y) { Console.WriteLine(y); } } public void M() { Action<int> arg_22_0; if (arg_22_0 = C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 == null) { C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 = new Action<int>(C.<>c__DisplayClass0.CS$<>9__inst.<M>b__1); } } }

Ahora podemos ver que el delegado ahora se eleva a una clase privada dentro de C , un comportamiento similar al que estamos acostumbrados a ver cuando se cierra sobre una variable / campo de instancia (cierre).

Sé que este es un detalle de implementación que puede estar sujeto a cambios en cualquier momento.

Aún así, me pregunto, ¿cuáles son los beneficios de elevar al delegado a una nueva clase y guardarlo en caché allí simplemente almacenándolo en caché en el sitio de la llamada?

Editar:

Este problema habla sobre el mismo comportamiento que se pregunta aquí.


Aún así, me pregunto, ¿cuáles son los beneficios de elevar al delegado a una nueva clase y guardarlo en caché allí simplemente almacenándolo en caché en el sitio de la llamada?

Te has perdido otro detalle realmente importante: ahora es un método de instancia. Creo que esa es la clave aquí. IIRC, se descubrió que invocar a un delegado que estaba "respaldado" por un método de instancia era más rápido que invocar a un delegado respaldado por un método estático, que es la motivación detrás del cambio.

Todo esto es un rumor, vagamente recordado por pasar tiempo con Dustin Campbell y Kevin Pilch-Bisson (ambos del equipo de Roslyn) en CodeMash, pero tendría sentido dado el código que ha mostrado.

(No he validado la diferencia de rendimiento para mí, y parece que está al revés ... pero los componentes internos de CLR pueden ser divertidos así ...)


Si. La parte más importante es que el método que contiene la implementación lambda es ahora un método de instancia.

Puede ver a un delegado como un intermediario que recibe una llamada de instancia a través de Invoke y despacha esa llamada de acuerdo con la convención de llamada del método de implementación.

Tenga en cuenta que existen requisitos ABI de la plataforma que especifican cómo se pasan los argumentos, cómo se devuelven los resultados, qué argumentos se pasan a través de registros y en cuáles, cómo se pasa "esto", etc. La violación de estas reglas puede tener un impacto negativo en las herramientas que dependen de la ejecución de la pila, como los depuradores.

Ahora, si el método de implementación es un método de instancia, lo único que debe suceder dentro del delegado es parchear "this", que es la instancia de delegado en el momento de Invoke, para ser el objeto Target incluido. En ese punto, dado que todo lo demás ya está donde debe estar, el delegado puede saltar directamente al cuerpo del método de implementación. En muchos casos, esto es notablemente menos trabajo de lo que tendría que suceder si el método de implementación fuera un método estático.