Страницы

Поиск по вопросам

суббота, 30 ноября 2019 г.

Кэшированый делегат в C#

#c_sharp #net


Что значит фраза "делегат кэшируется" в этом контексте (и вообще, что такое "кэшированный
делегат и для чего он): 

"В случае вызова SomeMethod(OtherMethod) — будет всегда создаваться делегат. 
В случае вызова SomeMethod(x => OtherMethod(x)) — делегат будет кешироваться."?

Контекст отсюда

UPD

Между первым вызовом и вторым есть разница в MSIL'е, а именно такой код:

static void SomeMethod(Func otherMethod)
{
    otherMethod(1);
}

static int OtherMethod(int x)
{
    return x;
}

static void Main(string[] args)
{
    SomeMethod(OtherMethod); // 1
    SomeMethod(x => OtherMethod(x)); // 2
    SomeMethod(x => OtherMethod(x)); // 3
}


Будет преобразован примерно в следующий:

static void Main()
{
    SomeMethod(new Func(OtherMethod));
    if (C.foo != null)
        SomeMethod(C.foo)
    else
    {
        C.foo = new Func(c, C.b)
        SomeMethod(C.foo);
    }
    if (C.foo1 != null)
        SomeMethod(C.foo1)
    else
    {
        C.foo1 = new Func(c, C.b1)
        SomeMethod(C.foo1);
    }
}
[CompileGenerated]
class C
{
    public static C c;
    public static Func foo;
    public static Func foo1;
    static C()
    {
        c = new C();
    }
    C(){}
    public int b(int x)
    {
        return OtherMethod(x);
    }
    public int b1(int x)
    {
        return OtherMethod(x);
    }
}


Но как видно, компилятор на 3 вызов создал и инициализировал новую "кэшированную"
переменную, а не использовал старую 
    


Ответы

Ответ 1



Обсуждаемая тема — особенность кодогенерации текущей версии майкрософтовского фреймворка .NET. Обсуждаемый код таков: class Program { static void SomeMethod(Func otherMethod) { otherMethod(1); } static int OtherMethod(int x) { return x; } static void Main(string[] args) { SomeMethod(OtherMethod); // 1 SomeMethod(x => OtherMethod(x)); // 2 } } При этом на текущий момент вызов SomeMethod(OtherMethod); // 1 компилируется в аналог такого: SomeMethod(new Func(Program.OtherMethod)); (дело в том, что OtherMethod — не делегат, а метод, вот компилятор и любезно подставил создание делегата). А вызов SomeMethod(x => OtherMethod(x)); // 2 — в аналог такого (по поводу LambdaClass смотрите здесь): Func f = LambdaClass.cached_f; // cached_f - невидимое статическое поле в классе LambdaClass, который содержит лямбду if (f == null) { f = LambdaClass.cached_f = new Func(LambdaClass.method_f); } Program.SomeMethod(f); То есть значение делегата (Func) и вправду кешируется. Но я бы не придавал этому факту большого значения: кодогенерация даже в Microsoft .NET меняется со временем (вот недавние изменения в кодогенерации лямбд, а вот недоумение команды, которая рассчитывала в своём коде на недокументированные особенности), а уж кодогенерация в других имплементациях имеет право отличаться и подавно.

Комментариев нет:

Отправить комментарий