Отключает ли приведение анонимной лямбды к строго типизированному делегату кэширование компилятора? - PullRequest
4 голосов
/ 27 марта 2019

Я пытаюсь понять крайний случай кэширования делегатов на основе компилятора, чтобы избежать выделения памяти.

Например, насколько я понимаю, этот делегат кэшируется для одного экземпляра и используется повторно, потому что он не закрывается ни по одной локальной переменной:

int[] set = new [] { 1, 2, 3, 4, 5, 6 };
var subset = set.Where(x => x % 2 == 0);

Теперь у меня есть несколько случаев, когда мой сгенерированный код может захотеть вызвать делегат напрямую, поэтому анонимный метод не является допустимым C #, например:

var result = (x => x % 2 == 0).Invoke(5); // Invalid

Чтобы обойти это, я вижу два варианта:

  1. Использование конструктора:
var result = (new Func<int, bool>(x => x % 2 == 0)).Invoke(5);
  1. Приведение анонимного делегата:
var result = ((Func<int, bool>)(x => x % 2 == 0)).Invoke(5);

Я предполагаю, что компилятор не будет кэшировать делегат в опции # 1, но я не уверен, будет ли это в # 2.

Это где-нибудь задокументировано?

1 Ответ

9 голосов
/ 27 марта 2019

Я предполагаю, что компилятор не будет кэшировать делегат в опции # 1, но я не уверен, что это будет в # 2.

На самом деле это может и в обоихслучаи, и они связаны друг с другом.

Из спецификации ECMA C # 5, раздел 7.6.10.5:

Обработка времени привязки выражения-создания-делегатаform new D (E), где D - тип делегата, а E - выражение, состоит из следующих шагов:

  • ...
  • Если E - анонимная функциявыражение создания делегата обрабатывается так же, как преобразование анонимной функции (§6.5) из E в D.
  • ...

Таким образом, в основном дваобрабатываются таким же образом.И в обоих случаях это может быть кэшировано.И да, очень странно, что «новое не обязательно означает новое».

Чтобы показать это, давайте напишем довольно тривиальную программу:

using System;

public class Program
{
    public static void Main()
    {
        var func = new Func<int, bool>(x => x % 2 == 0);
    }
}

Вот IL для Main метод на моей машине (по общему признанию, сборка с предварительным компилятором C # 8, но я некоторое время ожидаю того же):

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       29 (0x1d)
  .maxstack  8
  IL_0000:  ldsfld     class [mscorlib]System.Func`2<int32,bool> Program/'<>c'::'<>9__0_0'
  IL_0005:  brtrue.s   IL_001c
  IL_0007:  ldsfld     class Program/'<>c' Program/'<>c'::'<>9'
  IL_000c:  ldftn      instance bool Program/'<>c'::'<Main>b__0_0'(int32)
  IL_0012:  newobj     instance void class [mscorlib]System.Func`2<int32,bool>::.ctor(object,
                                                                                      native int)
  IL_0017:  stsfld     class [mscorlib]System.Func`2<int32,bool> Program/'<>c'::'<>9__0_0'
  IL_001c:  ret
} // end of method Program::Main

Это эффективно:

Func<int, bool> func;
func = cache;
if (func == null)
{
    func = new Func<int, bool>(GeneratedPrivateMethod);
    cache = func;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...