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

335
31 января 2017, 19:57

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

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

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

UPD

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

static void SomeMethod(Func<int, int> 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<int, int>(OtherMethod));
    if (C.foo != null)
        SomeMethod(C.foo)
    else
    {
        C.foo = new Func<int, int>(c, C.b)
        SomeMethod(C.foo);
    }
    if (C.foo1 != null)
        SomeMethod(C.foo1)
    else
    {
        C.foo1 = new Func<int, int>(c, C.b1)
        SomeMethod(C.foo1);
    }
}
[CompileGenerated]
class C
{
    public static C c;
    public static Func<int, int> foo;
    public static Func<int, int> foo1;
    static C()
    {
        c = new C();
    }
    C(){}
    public int b(int x)
    {
        return OtherMethod(x);
    }
    public int b1(int x)
    {
        return OtherMethod(x);
    }
}

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

Answer 1

Обсуждаемая тема — особенность кодогенерации текущей версии майкрософтовского фреймворка .NET.

Обсуждаемый код таков:

class Program
{
    static void SomeMethod(Func<int, int> 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<int, int>(Program.OtherMethod));

(дело в том, что OtherMethod — не делегат, а метод, вот компилятор и любезно подставил создание делегата). А вызов

SomeMethod(x => OtherMethod(x)); // 2

— в аналог такого (по поводу LambdaClass смотрите здесь):

Func<int, int> f = LambdaClass.cached_f;
// cached_f - невидимое статическое поле в классе LambdaClass, который содержит лямбду
if (f == null)
{
    f = LambdaClass.cached_f = new Func<int, int>(LambdaClass.method_f);
}
Program.SomeMethod(f);

То есть значение делегата (Func<int, int>) и вправду кешируется.

Но я бы не придавал этому факту большого значения: кодогенерация даже в Microsoft .NET меняется со временем (вот недавние изменения в кодогенерации лямбд, а вот недоумение команды, которая рассчитывала в своём коде на недокументированные особенности), а уж кодогенерация в других имплементациях имеет право отличаться и подавно.

READ ALSO
WPF. Messagebox. Как поменять стиль диалогового окна?

WPF. Messagebox. Как поменять стиль диалогового окна?

Стандартный стиль не вписывается в мой дизайн, а вся валидация идет через MessageboxShow("Сообщение")

753
получить элемент списка xaml

получить элемент списка xaml

есть список lisview с кнопками внутрикак можно получить элемент списка и объект данных, кликнув по какой-либо кнопке внутри него ?

371
Отключение кнопки WPF

Отключение кнопки WPF

Есть некоторая кнопка, которая создана от класса UserControl(то есть является не стандартной)И мне нужно, что бы когда свойство этой кнопки IsEnabled...

436