IL-код callvirt
используется для виртуальных методов, чтобы гарантировать, что вызывается правильное переопределение. Есть довольно хорошее объяснение разницы между call
и callvirt
здесь или более подробное техническое обсуждение здесь .
У каждого класса есть таблица методов (известная как Таблица виртуальных методов или VTable), которая содержит указатели на точный код, который нужно выполнить для каждого метода, определенного в классе и его родителях. Вместо того, чтобы содержать запись для каждой перегруженной версии метода через иерархию, он содержит один указатель метода для каждой сигнатуры метода. Когда класс переопределяет виртуальный метод, этот класс получает другой указатель метода в слоте метода.
callvirt
и call
принимают токен метаданных метода в качестве параметра и используют свойства этого токена, чтобы определить желаемый метод и найти его. callvirt
будет использовать VTable для поиска метода, который будет использоваться на основе предоставленных метаданных метода, всегда получая наиболее производную версию, действительную для типа объекта, для которого вызывается метод. call
, с другой стороны, вызовет указанный вами метод, который полезен, когда вам нужно выполнить base.Mover(a, b)
без зацикливания в бесконечном цикле.
Таким образом, чтобы найти текущую наилучшую перегрузку, нам нужны только метаданные для исходного определения virtual
или abstract
, поскольку все переопределения будут занимать один и тот же слот в таблице VTable для своих соответствующих классов. Единственный раз, когда он использует что-либо кроме базы, это когда вы используете call
для вызова определенного переопределения.
Просто для того, чтобы всем было легко запутаться, похоже, что C#
будет использовать callvirt
для вызова практически любого нестатического метода для ссылочного типа, кроме вызовов base.method()
типа. Все обычные вызовы методов (включая свойство get / set) для экземпляров классов используют callvirt
. Так было с первых дней создания прототипа C # в 1999 , по-видимому, и почти наверняка (есть нечеткий язык) из-за того, что callvirt
делает дополнительную проверку указателя this
, чтобы убедиться, что он не null
.