Работает ли? .Invoke (x) так же, как null-check + direct call - PullRequest
0 голосов
/ 25 октября 2018

Visual Studio блеет о том, что «вызов делегата может быть упрощен», когда у вас есть такие утверждения, как:

Action<int> foo = x;
if (foo != null)
    foo(10);

Правила смарт-тегов быстрого действия требуют, чтобы вы изменили это на:

Action<int> foo = x;
foo?.Invoke(10);

Компилятор хорошо с этим справляется и создает тот же код в любом случае?Или последний работает по-другому?

1 Ответ

0 голосов
/ 25 октября 2018

В сборке с отключенной оптимизацией (обычно отладочные сборки) вы получите следующие две последовательности инструкций IL:

IL_0000:  nop                               IL_0000:  nop         
IL_0001:  ldnull                            IL_0001:  ldnull      
IL_0002:  ldftn       x                     IL_0002:  ldftn       x
IL_0008:  newobj      Action<int>..ctor     IL_0008:  newobj      Action<int>..ctor
IL_000D:  stloc.0     // foo                IL_000D:  stloc.0     // foo
IL_000E:  ldloc.0     // foo                IL_000E:  ldloc.0     // foo
IL_000F:  ldnull                            IL_000F:  brtrue.s    IL_0013
IL_0010:  cgt.un                            IL_0011:  br.s        IL_001C
IL_0012:  stloc.1     
IL_0013:  ldloc.1     
IL_0014:  brfalse.s   IL_001F
IL_0016:  ldloc.0     // foo                IL_0013:  ldloc.0     // foo
IL_0017:  ldc.i4.s    0A                    IL_0014:  ldc.i4.s    0A 
IL_0019:  callvirt    Action<int>.Invoke    IL_0016:  callvirt    Action<int>.Invoke
IL_001E:  nop                               IL_001B:  nop         
IL_001F:  ret                               IL_001C:  ret 

Здесь есть небольшие различия в отношении инструкций ветвления, но давайте собирать с включенной оптимизацией (обычноВыпуск сборки):

IL_0000:  ldnull                            IL_0000:  ldnull      
IL_0001:  ldftn       x                     IL_0001:  ldftn       x
IL_0007:  newobj      Action<int>..ctor     IL_0007:  newobj      Action<int>..ctor
IL_000C:  stloc.0     // foo                IL_000C:  dup         
IL_000D:  ldloc.0     // foo                IL_000D:  brtrue.s    IL_0011
IL_000E:  brfalse.s   IL_0018               IL_000F:  pop         
IL_0010:  ldloc.0     // foo                IL_0010:  ret         
IL_0011:  ldc.i4.s    0A                    IL_0011:  ldc.i4.s    0A 
IL_0013:  callvirt    Action<int>.Invoke    IL_0013:  callvirt    Action<int>.Invoke
IL_0018:  ret                               IL_0018:  ret 

Опять небольшая разница в инструкциях ветки.В частности, в примере, использующем оператор объединения нулей, будет помещен дубликат ссылки делегата действия в стеке, тогда как в примере с оператором if будет использоваться временная локальная переменная.JITter может поместить оба в регистр, однако, это не является окончательным, что он будет вести себя по-разному.

Давайте попробуем что-то другое:

public static void Action1(Action<int> foo)
{
    if (foo != null)
        foo(10);
}

public static void Action2(Action<int> foo)
{
    foo?.Invoke(10);
}

Это компилируется (опять же, с оптимизациейвключено) на:

IL_0000:  ldarg.0                           IL_0000:  ldarg.0     
IL_0001:  brfalse.s   IL_000B               IL_0001:  brfalse.s   IL_000B
IL_0003:  ldarg.0                           IL_0003:  ldarg.0     
IL_0004:  ldc.i4.s    0A                    IL_0004:  ldc.i4.s    0A 
IL_0006:  callvirt    Action<int>.Invoke    IL_0006:  callvirt    Action<int>.Invoke
IL_000B:  ret                               IL_000B:  ret  

Точно такой же код .Таким образом, различия в вышеприведенных примерах отличались из-за других вещей, кроме оператора слияния нуля.

Теперь, чтобы ответить на ваш конкретный вопрос, будут ли отличия последовательности ветвей от вашего примера влиять на производительность? Единственный способ узнать это - на самом деле тестировать. Однако , я был бы очень удивлен, если это окажется чем-то, что вам нужно принять во внимание.Вместо этого я бы выбрал стиль кода в зависимости от того, что вам проще всего написать, прочитать и понять.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...