Если у меня есть метод, который возвращает значение (например, метод Remove
класса Dictionary
возвращает bool
), что произойдет, если я не присвою возвращаемое значение переменной? Другими словами, если я напишу dictionary.Remove("plugin-01");
без присвоения результата переменной bool
, каковы различия в компиляции по отношению к bool b = dictionary.Remove("plugin-01");
?Как скомпилированные вызовы метода возвращают значение?
ответ
Давайте рассмотрим простой пример, и код 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
, чтобы присвоить результат переменной.
Так что, если вам не нужны результаты вызова метода, вы должны просто проигнорировать результат. Даже если вы назначаете результат переменной и эта переменная никогда не используется, компилятор может полностью оптимизировать переменную и назначение - по крайней мере, в режиме деблокирования (в режиме отладки большинство оптимизаций отключены для улучшения вашего отладочного опыта).
Возвращаемое значение будет просто проигнорировано, если вы его не используете.
Вы могли бы сделать что-то вроде
if(!dictionary.Remove("plugin-01")) {
MessageBox.Show("Error: plugin-01 does not exist!");
}
Если вы не заботитесь, вы можете смело писать
dictionary.Remove("plugin-01");
Часть вызова такая же, но часть назначения пропускается в ИЛ. Посмотрите - вот разборку простой программы:
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
Верхние четыре линии являются общими; последние три строки первого вызова связаны с назначением; они отсутствуют в разборке для второго вызова.
- 1. Системные вызовы Linux возвращают значение
- 2. Последующие вызовы httpClient возвращают исходные вызовы
- 3. вызовы метода общего класса
- 4. C# BackgoundWorker возвращают значение
- 5. Как тестировать вызовы метода класса?
- 6. Как перехватить вызовы метода экземпляра?
- 7. Have Twig активов() вызовы возвращают относительный URL
- 8. Log вызовы возвращают NaN в C
- 9. Ожидайте множественные вызовы метода
- 10. Repository не возвращают значение из моего метода испытаний
- 11. объектов Override возвращают значение
- 12. AppConfig возвращают нулевое значение
- 13. sharedPreferences возвращают нулевое значение
- 14. JsonArray возвращают нулевое значение
- 15. экспедиторская Шаблон вызовы метода-члена
- 16. stubbing out статические вызовы метода
- 17. Каскадные вызовы метода в FitNess?
- 18. Как настроить перегруженные вызовы метода в Moq?
- 19. Как преобразовать вызовы метода в постфиксную нотацию?
- 20. Как отслеживать все вызовы метода из класса?
- 21. PHPUnit - как тестировать вызовы метода сиблинга?
- 22. Как реализовать вызовы метода Async в Java?
- 23. как перевести запрос linq на вызовы метода
- 24. Rhino.Mocks, как тестировать вызовы метода абстрактного класса
- 25. Как получить рекурсивные вызовы от метода
- 26. Ajax вызовы метода Web API
- 27. Finagle + Thrift: вызовы метода подсчета
- 28. Лямбда выражения и вызовы метода
- 29. Как перехватывать вызовы метода класса, а не только вызовы метода экземпляра
- 30. JavaScript возвращают значение по умолчанию
Если вы удалите 'Console.WriteLine (foo)', должен ли IL быть равен первому примеру? – enzom83
Да, это - он оптимизирован (предположительно), потому что переменная 'foo' никогда не используется. – BrokenGlass
В вашем первом «Это производит следующий IL», я думаю, вы должны включить инструкцию POP, которая следует за вызовом. Это поясняет, что результат метода находится в стеке MSIL, и вызывающий абонент должен каким-то образом его обрабатывать (даже если «обработка» означает явно его выброс) –