Виртуализация в конструкторе Super Class - PullRequest
4 голосов
/ 21 ноября 2008

Я придерживался мнения, что виртуализация не работает в конструкторе суперкласса в соответствии с дизайном ООП. Например, рассмотрим следующий код C #.

using System;
namespace Problem
{
    public class BaseClass 
    {
        public BaseClass() 
        {
            Console.WriteLine("Hello, World!");
            this.PrintRandom();
        }
        public virtual void PrintRandom() 
        {
            Console.WriteLine("0");
        }
    }

    public class Descendent : BaseClass 
    {
        private Random randomValue;
        public Descendent() 
        {
            Console.WriteLine("Bonjour, Monde!");
            randomValue = new Random();
        }
        public override void PrintRandom() 
        {
            Console.WriteLine(randomValue.NextDouble().ToString());
        }

        public static void Main() 
        {
            Descendent obj = new Descendent();
            obj.PrintRandom();
            Console.ReadLine();
        }
    }
}

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

Аналогичный код работает в C ++, потому что вызов PrintRandom не перенаправляется в производный класс со времен IMO, а порядок в C ++ выглядит примерно так:


1. вызов конструктора базового класса
2. Обновление V - Таблица для этого класса
3. вызвать код конструктора

Мой вопрос заключается в том, что, во-первых, прав ли я, что согласно принципам ООП виртуализация не должна / не работает в конструкторе суперкласса, а во-вторых, если я прав, то почему поведение отличается во всех языках .NET? (Я проверил это с C #, VB.NET и MC ++)

Ответы [ 3 ]

4 голосов
/ 21 ноября 2008

В нативном C ++ программа работает должным образом: вы получаете вызов версии виртуального функции базового класса в конструкторе базового класса. Во время вызова конструктора существуют только базовый класс и его виртуальные функции, поэтому вы получаете версию виртуальной функции самого низкого уровня, определенную в то время. Это не означает, что виртуализация не может быть использована, вы просто не получите версии подкласса виртуальных методов в конструкторах базовых классов (вот почему это не рекомендуется).

Очевидно, что, как видите, управляемый код работает по-разному, поскольку (iirc) весь объект создается до вызова конструкторов, и, таким образом, вы получаете виртуальную функцию подкласса перед конструктором подкласса. Это документированное различие между поведением языков, но должно быть согласованным для всех языков .NET (поскольку все они компилируются в один и тот же IL).

3 голосов
/ 21 ноября 2008

На мой взгляд, это не вопрос ОО-принципов - вопрос о том, как он справляется с этой конкретной головоломкой, зависит от платформы. Вызывать виртуальный метод из конструктора не рекомендуется именно по этой причине, однако - если вы собираетесь это сделать, вам нужно явно указать очень , что вы собираетесь его вызывать, чтобы любой класс он знает, чего ожидать.

Java использует тот же подход, что и .NET , за исключением , что в C # любые инициализаторы переменных экземпляра выполняются до вызова базового конструктора. Это означает, что в вашем конкретном примере вы можете исправить код, инициализируя random в точке объявления. На Java это не поможет.

Что касается того, почему MC ++ не работает таким образом, я не знаю - я предлагаю вам сравнить сгенерированный IL. Я предполагаю, что он явно вызывает не виртуальный вызов метода.

РЕДАКТИРОВАТЬ: я подозреваю, что я неправильно понял вопрос - как работает MC ++? Если это работает так, как работает C #, это хорошо, IMO, обеспечивая согласованный взгляд на платформу .NET.

0 голосов
/ 21 ноября 2008

Я бы предложил использовать FxCop в вашем коде. Я работал со многими людьми, которые отклоняют пункты, поднятые этим инструментом, как несущественные, но, если в вашем коде много мелких проблем (например, ваших), то вероятность быть укушенным одним или несколькими из них намного выше.

Анализ кода ReSharper также поднимет эту конкретную проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...