Код ILDASM для перегруженных методов при использовании полиморфизма - PullRequest
0 голосов
/ 21 января 2019

Когда я выполняю следующий код, в ILDASM показывается следующее.Я сомневаюсь, что хотя метод дочернего класса будет выполнен, почему ILDASM показывает, что вызывается метод Baseclass.Это потому, что экземпляр имеет тип BaseClass?Если это причина, как базовый класс обращается к методу дочернего класса?

 class BaseClass
    {
        public virtual void Mover(long a,long b)
        {
           Console.WriteLine(a*b+"LongBaseClass");
        }
    }
class ChildClass : BaseClass
{
    public override void Mover(long a, long b)
    {
        Console.WriteLine(a+b+"LongChildClass");
    }
    public static void Main(string[] args)
    {
       long a=10,b=20;
       BaseClass bcc=new ChildClass();
       bcc.Mover(a,b);
}
callvirt instance void practiceonly.BaseClass::Mover(int64, int64)

Ответы [ 2 ]

0 голосов
/ 21 января 2019

[E] ven, хотя метод дочернего класса будет выполнен, почему ILDASM показывает, что вызывается метод Baseclass?

Поскольку ILDASM показывает, как выглядит код во время компиляции , а переопределение метода разрешено во время выполнения . Независимо от того, звоните ли вы

BaseClass baseClass = new ChildClass();
baseClass.Mover(42, 42); // Yields 1764LongBaseClass

или

ChildClass childClass = new ChildClass();
childClass.Mover(42, 42); // Yields 1764LongChildClass

оба переведут на

callvirt instance void BaseClass::Mover(int64, int64)

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

Какой метод в действительности вызывается, определяется во время выполнения на основе объекта, на который ссылается ссылка (в последнем примере кода ссылка имеет тип ChildClass, тогда как в первом она имеет тип BaseClass).


В .NET существует разница между статическим или полиморфизмом времени компиляции и динамическим или run- полиморфизм времени . Переопределением метода является динамический полиморфизм. Примером статического полиморфизма может быть перегрузка метода.

0 голосов
/ 21 января 2019

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.

...