Испускание вызова функции делегата

У меня есть следующий код С#:

public static double f2(Func<double, double> f, double x)
{
    return f(x);
}   

А вот и IL-код:

.method public hidebysig static 
    float64 f2 (
        class [mscorlib]System.Func`2<float64, float64> f,
        float64 x
    ) cil managed 
{
    // Method begins at RVA 0x20bd
    // Code size 8 (0x8)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldarg.1
    IL_0002: callvirt instance !1 class [mscorlib]System.Func`2<float64, float64>::Invoke(!0)
    IL_0007: ret
}

Как я могу излучать

callvirt instance !1 class [mscorlib]System.Func`2<float64, float64>::Invoke(!0)

через System.Reflection.Emit или лучше через Mono.Cecil?

Что означают !1 и !0?


person Ivan Kochurkin    schedule 09.10.2012    source источник


Ответы (1)


Синтаксис !n является ссылкой на общий аргумент.

В этом примере...

!0 — это ссылка на первый универсальный аргумент Func<double, double> (используется как тип аргумента метода Invoke).

!1 — это ссылка на второй общий универсальный аргумент Func<double, double> (используется как возвращаемый тип Invoke).

EDIT: ваш метод с использованием System.Reflection.Emit...

var dynamicMethod = new DynamicMethod(
    "f2Dynamic", 
    typeof(double), 
    new Type[] { typeof(Func<double, double>), typeof(double) });

var il = dynamicMethod.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Callvirt, typeof(Func<double, double>).GetMethod("Invoke"));
il.Emit(OpCodes.Ret);

var f2Dynamic = 
    (Func<Func<double, double>, double, double>)dynamicMethod.CreateDelegate(
        typeof(Func<Func<double, double>, double, double>));

Console.WriteLine(f2(x => x * x, 10.0));        // prints 100
Console.WriteLine(f2Dynamic(x => x * x, 10.0)); // prints 100

EDIT2: исправлено объяснение !n после подсказки @kvb.

person ulrichb    schedule 09.10.2012
comment
Ваш код System.Reflection.Emit верен, но ваше объяснение !0 и !1 нет - они относятся к параметрам универсального типа System.Func<,>, а не к стеку. - person kvb; 10.10.2012
comment
Спасибо. Я не знал, что это так просто :) Также, пожалуйста, исправьте свой ответ с примечанием @kvb. - person Ivan Kochurkin; 11.10.2012
comment
@kvb Ты действительно уверен? Если да, то что означает callvirt instance !1, что означает Invoke(!0). Любая ссылка? Кроме того, кажется, что это специальный синтаксис Reflector, ILSpy и вывод LINQPad IL не имеют этого синтаксиса !n. - person ulrichb; 11.10.2012
comment
ILSpy и Microsoft IL Dasm, безусловно, имеют синтаксис !n. - person Ivan Kochurkin; 11.10.2012
comment
Я даже скажу больше: листинг IL из вопроса от ILSpy. - person Ivan Kochurkin; 11.10.2012
comment
@ulrichb - Да, я уверен. !1 — это возвращаемый тип метода, а !0 — это тип аргумента для Invoke. В неуниверсальном методе вы увидите что-то вроде callvirt instance float64 class ...::Invoke(float64). Поскольку Func<,> является универсальным, аргументы универсального типа заменяют конкретный параметр и возвращаемые типы. - person kvb; 11.10.2012
comment
@kvb Отредактировано. Большое спасибо за ваши подсказки, чтобы исправить мой ответ. - person ulrichb; 12.10.2012