usos usar sirve qué programacion porque para los funcionan delegate delegados delegado concepto como c# delegates cil

c# - usar - ¿Por qué el compilador agrega un parámetro adicional para los delegados cuando no hay cierre?



para qué sirve un delegado c# (1)

Aunque esto puede parecer muy sorprendente, una búsqueda rápida muestra que es por razones de rendimiento.

En un informe de error al respecto , se señala que los delegados que no tienen implícito this son considerablemente más lentos que los delegados que sí lo tienen implícitamente, porque los delegados que no tienen implícito this necesitan hacer un poco de complicados argumentos para barajar. Se invoca al delegado:

Supongamos que llamas func1(1, 2) . Esto parece (pseudo-código, no CIL)

push func1 push 1 push 2 call Func<,,>::Invoke

Cuando se sabe que esta func1 está vinculada a una función estática que toma dos valores int , debe realizar el equivalente de cualquiera

push arg.1 push arg.2 call method

o

arg.0 = arg.1 arg.1 = arg.2 jmp method

Mientras que se sabe que func1 está vinculado a una función estática que toma valores null y dos valores int , solo necesita realizar el equivalente de

arg.0 = null jmp method

ya que el entorno ya está configurado perfectamente para ingresar a una función tomando un tipo de referencia y dos valores int .

Sí, es una microoptimización que normalmente no importa, pero es una de la que todos se benefician, incluidos aquellos en situaciones en las que sí importa.

Estaba jugando con delegates y noté que cuando creo un Func<int,int,int> como el siguiente ejemplo:

Func<int, int, int> func1 = (x, y) => x * y;

La firma del método generado por el compilador no es lo que esperaba:

Como puedes ver, toma un objeto para su primer parámetro. Pero cuando hay un cierre:

int z = 10; Func<int, int, int> func1 = (x, y) => x * y * z;

Todo funciona como se espera:

Este es el código IL para el método con parámetro extra:

.method private hidebysig static int32 ''<Main>b__0''(object A_0, int32 x, int32 y) cil managed { .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) // Code size 8 (0x8) .maxstack 2 .locals init ([0] int32 V_0) IL_0000: ldarg.1 IL_0001: ldarg.2 IL_0002: mul IL_0003: stloc.0 IL_0004: br.s IL_0006 IL_0006: ldloc.0 IL_0007: ret } // end of method Program::''<Main>b__0''

Parece que el parámetro A_0 ni siquiera se usa. Entonces, ¿cuál es el propósito del parámetro object en el primer caso? ¿Por qué no se agrega cuando hay un cierre?

Nota: Si tiene una mejor idea para el título, no dude en editarlo.

Nota 2: Compilé el primer código en los modos Debug y Release , no hubo diferencia. Pero compilé el segundo en modo Debug para obtener un comportamiento de cierre, ya que optimiza la variable local en el modo Release .

Nota 3: Estoy usando Visual Studio 2014 CTP .

Editar: Este es el código generado para Main en el primer caso:

.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 30 (0x1e) .maxstack 2 .locals init ([0] class [mscorlib]System.Func`3<int32,int32,int32> func1) IL_0000: nop IL_0001: ldsfld class [mscorlib]System.Func`3<int32,int32,int32> ConsoleApplication9.Program::''CS$<>9__CachedAnonymousMethodDelegate1'' IL_0006: dup IL_0007: brtrue.s IL_001c IL_0009: pop IL_000a: ldnull IL_000b: ldftn int32 ConsoleApplication9.Program::''<Main>b__0''(object, int32, int32) IL_0011: newobj instance void class [mscorlib]System.Func`3<int32,int32,int32>::.ctor(object, native int) IL_0016: dup IL_0017: stsfld class [mscorlib]System.Func`3<int32,int32,int32> ConsoleApplication9.Program::''CS$<>9__CachedAnonymousMethodDelegate1'' IL_001c: stloc.0 IL_001d: ret } // end of method Program::Main