Как скомпилированные вызовы методов возвращают значение? - PullRequest
5 голосов
/ 25 февраля 2012

Если у меня есть метод, который возвращает значение (например, метод Remove класса Dictionary возвращает bool), что произойдет, если я не назначу возвращаемое значение переменной? Другими словами, если я пишу dictionary.Remove("plugin-01"); без присвоения результата переменной bool, каковы различия в компиляции относительно bool b = dictionary.Remove("plugin-01");?

Ответы [ 3 ]

15 голосов
/ 25 февраля 2012

Давайте рассмотрим простой пример и код IL, который он генерирует (любезно предоставлено LinqPad)

void Main()
{
    Bar();
    Baz();
}

bool Bar()
{
  return true;
}

void Baz()
{
  Console.WriteLine("placeholder");
}

Это создает следующий IL:

IL_0000:  ldarg.0     
IL_0001:  call        UserQuery.Bar
IL_0006:  pop          //remove current value from evaluation stack
IL_0007:  ldarg.0     
IL_0008:  call        UserQuery.Baz


Bar:
IL_0000:  ldc.i4.1    
IL_0001:  ret    

Baz:
IL_0000:  ldstr       "placeholder"
IL_0005:  call        System.Console.WriteLine
IL_000A:  ret   

Вы видите Bar вызывается, а затем всплывает, чтобы удалить логическое возвращаемое значение из стека оценки - это ни к чему не приведет.Мне пришлось обновить пример, чтобы включить еще один вызов метода в Baz(), иначе всплывающее окно не будет отправлено, поскольку программа заканчивается.

Теперь давайте рассмотрим случай, когда мы фактически используем возвращаемое значение:

void Main()
{
    bool foo = Bar();
    Console.WriteLine(foo);
}

bool Bar()
{
  return true;
}

Это приводит к следующему IL:

IL_0000:  ldarg.0     
IL_0001:  call        UserQuery.Bar
IL_0006:  stloc.0    //pops current value from evaluation stack, stores in local var
IL_0007:  ldloc.0     
IL_0008:  call        System.Console.WriteLine

Bar:
IL_0000:  ldc.i4.1    
IL_0001:  ret 

Игнорировать часть System.Console.WriteLine, которая является всем после и включая IL_007 - просто пришлось добавить ее, чтобы компилятор не оптимизировал прочьиспользование переменной.Вы видите, что результат вызова метода Bar извлекается из стека оценки и сохраняется в локальной переменной foo.В этом разница - либо pop, который захватывает и отбрасывает возвращаемое значение, либо stloc.0 для присвоения результата переменной.

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

3 голосов
/ 25 февраля 2012

Возвращаемое значение будет просто игнорироваться, если вы его не используете.

Вы можете сделать что-то вроде

if(!dictionary.Remove("plugin-01")) {
    MessageBox.Show("Error: plugin-01 does not exist!");
}

Если вам все равно, вы можете смело писать

dictionary.Remove("plugin-01");
1 голос
/ 25 февраля 2012

Часть вызова такая же, но часть назначения пропускается в IL.Посмотрите - вот разборка простой программы:

var d = new Dictionary<int,int>();
bool a = d.Remove(5);
d.Remove(6);

Разборка выглядит следующим образом:

    bool a = d.Remove(5);
00000057  mov         ecx,dword ptr [ebp-40h] 
0000005a  mov         edx,5 
0000005f  cmp         dword ptr [ecx],ecx 
00000061  call        69106A00
// Here is the assignment part 
00000066  mov         dword ptr [ebp-4Ch],eax 
00000069  movzx       eax,byte ptr [ebp-4Ch] 
0000006d  mov         dword ptr [ebp-44h],eax 
    d.Remove(6);
00000070  mov         ecx,dword ptr [ebp-40h] 
00000073  mov         edx,6 
00000078  cmp         dword ptr [ecx],ecx 
0000007a  call        69106A00

Верхние четыре строки являются общими;последние три строки из первого вызова касаются назначения;они отсутствуют при разборке для второго вызова.

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