См. это старое сообщение в блоге Эрика Ганнерсона.
Вот текст поста:
Почему C # всегда использует callvirt?
Этот вопрос возник из-за внутреннего псевдонима C #, и я подумал, что ответ будет представлять общий интерес. Это при условии, что ответ правильный - прошло довольно много времени.
Язык .NET IL предоставляет инструкции call и callvirt, причем callvirt используется для вызова виртуальных функций. Но если вы посмотрите на код, который генерирует C #, вы увидите, что он генерирует «callvirt» даже в тех случаях, когда виртуальная функция не задействована. Почему он это делает?
Я вернулся к заметкам о дизайне языка, которые у меня есть, и они совершенно ясно заявляют, что мы решили использовать callvirt 13.12.1999. К сожалению, они не отражают наше обоснование для этого, поэтому мне придется уйти из моей памяти.
Мы получили отчет от кого-то (вероятно, одной из групп .NET, использующих C # (хотя в то время он еще не назывался C #)), который написал код, который вызывал метод с нулевым указателем, но они не сделали этого. не получите исключения, потому что метод не имеет доступа ни к каким полям (т. е. «this» было нулевым, но ничто в методе не использовало его). Затем этот метод вызвал другой метод, который использовал эту точку и выдал исключение, после чего возникло небольшое головокружение. После того, как они поняли это, они прислали нам записку об этом.
Мы подумали, что возможность вызова метода для нулевого экземпляра была немного странной. Питер Голде провел некоторое тестирование, чтобы увидеть, какое влияние всегда оказывал перфоманс на callvirt, и он был настолько мал, что мы решили внести изменения.