2012-02-25 3 views
4

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

ответ

13

Давайте рассмотрим простой пример, и код 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, чтобы присвоить результат переменной.

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

+0

Если вы удалите 'Console.WriteLine (foo)', должен ли IL быть равен первому примеру? – enzom83

+1

Да, это - он оптимизирован (предположительно), потому что переменная 'foo' никогда не используется. – BrokenGlass

+0

В вашем первом «Это производит следующий IL», я думаю, вы должны включить инструкцию POP, которая следует за вызовом. Это поясняет, что результат метода находится в стеке MSIL, и вызывающий абонент должен каким-то образом его обрабатывать (даже если «обработка» означает явно его выброс) –

3

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

Вы могли бы сделать что-то вроде

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

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

dictionary.Remove("plugin-01"); 
1

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

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 

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

Смежные вопросы