Страницы

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

четверг, 4 октября 2018 г.

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

Что значит фраза "делегат кэшируется" в этом контексте (и вообще, что такое "кэшированный делегат и для чего он):
"В случае вызова 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 вызов создал и инициализировал новую "кэшированную" переменную, а не использовал старую


Ответ

Обсуждаемая тема — особенность кодогенерации текущей версии майкрософтовского фреймворка .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 меняется со временем (вот недавние изменения в кодогенерации лямбд, а вот недоумение команды, которая рассчитывала в своём коде на недокументированные особенности), а уж кодогенерация в других имплементациях имеет право отличаться и подавно.

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

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