Почему компилятор C# испускает callvirt инструкцию для GetType () вызов метода?

Алгоритм основан на поиске маршрута между двумя точками, а другой проверяет, могут ли все точки быть связаны (имея маршрут между ними):

28
задан Andrew Hare 10 May 2009 в 16:56
поделиться

5 ответов

Just playing safe.

Technically C# compiler doesn't always use callvirt

For static methods & methods defined on value types, it uses call. The majority is provided via the callvirt IL instruction.

The difference that swung the vote between the two is the fact that call assumes the "object being used to make the call" is not null. callvirt on the other hand checks for not null and throws a NullReferenceException if required.

  • For static methods, the object is a type object and cannot be null. Ditto for value types. Hence call is used for them - better performance.
  • For the others, the language designers decided to go with callvirt so the JIT compiler verifies that the object being used to make the call is not null. Even for non-virtual instance methods.. they valued safety over performance.

See Also: Jeff Richter does a better job at this - in his 'Designing Types' chapter in CLR via C# 2nd Ed

22
ответ дан 28 November 2019 в 03:08
поделиться

См. это старое сообщение в блоге Эрика Ганнерсона.

Вот текст сообщения:

Почему C # всегда использует callvirt?

Этот вопрос придумал внутренний псевдоним C #, и я подумал, что ответ будет представлять общий интерес. Это при условии, что ответ правильный - прошло уже довольно много времени.

Язык .NET IL предоставляет как команду call, так и callvirt, причем callvirt используется для вызова виртуальных функций. Но если вы посмотрите код, который генерирует C #, вы увидите, что он генерирует «callvirt» даже в тех случаях, когда не задействована виртуальная функция. Почему он это делает?

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

Мы получили отчет от кого-то (вероятно, одной из групп .NET, использующих C # (думали, что это еще не было названо C # в то время)), которые написали код, который вызывал метод по нулевому указателю, но они не получили исключения, потому что метод не обращался к каким-либо полям (т.е. «this» было пустым, но ничто в методе не использовало его ). Затем этот метод вызвал другой метод, который действительно использовал точку this и вызвал исключение, что привело к некоторой головной боли. После того, как они это выяснили, они прислали нам сообщение об этом.

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

m придется уйти из моей памяти.

Мы получили отчет от кого-то (вероятно, одной из групп .NET, использующих C # (думала, что это еще не было C # в то время)), кто написал код, который вызывал метод с нулевым указателем, но они не получили исключения, потому что метод не имел доступа ни к каким полям (т.е. «this» было пустым, но ничто в методе его не использовало). Затем этот метод вызвал другой метод, который действительно использовал точку this и вызвал исключение, что привело к некоторой головной боли. После того, как они это выяснили, они прислали нам сообщение об этом.

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

m придется уйти из моей памяти.

Мы получили отчет от кого-то (вероятно, одной из групп .NET, использующих C # (думала, что это еще не было C # в то время)), кто написал код, который вызывал метод с нулевым указателем, но они не получили исключения, потому что метод не имел доступа ни к каким полям (т.е. «this» было пустым, но ничто в методе его не использовало). Затем этот метод вызвал другой метод, который действительно использовал точку this и вызвал исключение, что привело к некоторой головной боли. После того, как они это выяснили, они прислали нам сообщение об этом.

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

Мы получили отчет от кого-то (вероятно, одной из групп .NET, использующих C # (думали, что это еще не было C # в то время)), кто написал код, который вызывал метод по нулевому указателю, но они этого не сделали. не получить исключения, потому что метод не обращался ни к каким полям (т.е. «this» было пустым, но ничто в методе его не использовало). Затем этот метод вызвал другой метод, который действительно использовал точку this и вызвал исключение, что привело к некоторой головной боли. После того, как они это выяснили, они прислали нам сообщение об этом.

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

Мы получили отчет от кого-то (вероятно, одной из групп .NET, использующих C # (думали, что это еще не было C # в то время)), кто написал код, который вызывал метод по нулевому указателю, но они этого не сделали. не получить исключения, потому что метод не обращался ни к каким полям (т.е. «this» было пустым, но ничто в методе его не использовало). Затем этот метод вызвал другой метод, который действительно использовал точку this и вызвал исключение, что привело к некоторой головной боли. После того, как они это выяснили, они прислали нам сообщение об этом.

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

t в то время еще назывался C #)), которые написали код, который вызывал метод по нулевому указателю, но они не получили исключения, потому что метод не обращался ни к каким полям (т.е. «this» было пустым, но ничего в метод использовал это). Затем этот метод вызвал другой метод, который действительно использовал точку this и вызвал исключение, что привело к некоторой головной боли. После того, как они это выяснили, они прислали нам сообщение об этом.

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

t в то время еще назывался C #)), которые написали код, который вызывал метод по нулевому указателю, но они не получили исключения, потому что метод не обращался ни к каким полям (т.е. «this» было пустым, но ничего в метод использовал это). Затем этот метод вызвал другой метод, который действительно использовал точку this и вызвал исключение, что привело к некоторой головной боли. После того, как они это выяснили, они прислали нам сообщение об этом.

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

Затем этот метод вызвал другой метод, который действительно использовал точку this и вызвал исключение, что привело к некоторой головной боли. После того, как они это выяснили, они прислали нам сообщение об этом.

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

Затем этот метод вызвал другой метод, который действительно использовал точку this и вызвал исключение, что привело к некоторой головной боли. После того, как они это выяснили, они прислали нам сообщение об этом.

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

28
ответ дан 28 November 2019 в 03:08
поделиться

В качестве (возможно-) интересного в сторону ... GetType () необычен тем, что не виртуальный - это приводит к некоторым очень и очень странным вещам .

(помечено как wiki, поскольку это несколько не по теме фактического вопроса)

3
ответ дан 28 November 2019 в 03:08
поделиться

Компилятору неизвестен реальный тип o в первом выражении, но он знает реальный тип во втором выражении. Похоже, он просматривает только один оператор за раз.

Это нормально, потому что C # сильно зависит от JIT для оптимизации. Весьма вероятно, что в таком простом случае оба вызова станут вызовами экземпляра во время выполнения.

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

1
ответ дан 28 November 2019 в 03:08
поделиться

Я рискну предположить, что это потому, что первое присваивается переменной, которая потенциально может содержать экземпляр другого типа с пониженным преобразованием, который мог бы переопределить GetType (хотя мы видим, что это не так); второй никогда не мог быть ничем иным, кроме Объект .

0
ответ дан 28 November 2019 в 03:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: