Виртуальный член вызова в конструкторе - PullRequest
1226 голосов
/ 23 сентября 2008

Я получаю предупреждение от ReSharper о вызове виртуального члена от моего конструктора объектов.

Почему бы это не делать?

Ответы [ 17 ]

3 голосов
/ 05 октября 2017

Остерегайтесь слепого следования совету Решарпера и запечатывания класса! Если это модель в EF Code First, она удалит виртуальное ключевое слово, что отключит отложенную загрузку его отношений.

    public **virtual** User User{ get; set; }
3 голосов
/ 28 августа 2016

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

Родительский класс ниже пытается установить значение для виртуального члена в его конструкторе. И это вызовет повторное предупреждение, пусть посмотрим на код:

public class Parent
{
    public virtual object Obj{get;set;}
    public Parent()
    {
        // Re-sharper warning: this is open to change from 
        // inheriting class overriding virtual member
        this.Obj = new Object();
    }
}

Дочерний класс здесь переопределяет родительское свойство. Если это свойство не было помечено как виртуальное, компилятор предупредит, что это свойство скрывает свойство в родительском классе, и предложит добавить ключевое слово «new», если оно намеренно.

public class Child: Parent
{
    public Child():base()
    {
        this.Obj = "Something";
    }
    public override object Obj{get;set;}
}

Наконец, влияние на использование, вывод приведенного ниже примера отменяет начальное значение, установленное конструктором родительского класса. И это то, что Re-sharper пытается предупредить вас , значения, установленные в конструкторе родительского класса, открыты для перезаписи конструктором дочернего класса, который вызывается сразу после конструктора родительского класса .

public class Program
{
    public static void Main()
    {
        var child = new Child();
        // anything that is done on parent virtual member is destroyed
        Console.WriteLine(child.Obj);
        // Output: "Something"
    }
} 
3 голосов
/ 14 августа 2015

Один важный пропущенный бит: как правильно решить эту проблему?

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

Следующий код, взятый из Руководства по проектированию MSDN , демонстрирует эту проблему.

public class BadBaseClass
{
    protected string state;

    public BadBaseClass()
    {
        this.state = "BadBaseClass";
        this.DisplayState();
    }

    public virtual void DisplayState()
    {
    }
}

public class DerivedFromBad : BadBaseClass
{
    public DerivedFromBad()
    {
        this.state = "DerivedFromBad";
    }

    public override void DisplayState()
    {   
        Console.WriteLine(this.state);
    }
}

Когда создается новый экземпляр DerivedFromBad, конструктор базового класса вызывает DisplayState и показывает BadBaseClass, поскольку поле еще не было обновлено производным конструктором.

public class Tester
{
    public static void Main()
    {
        var bad = new DerivedFromBad();
    }
}

Улучшенная реализация удаляет виртуальный метод из конструктора базового класса и использует метод Initialize. При создании нового экземпляра DerivedFromBetter отображается ожидаемый «DerivedFromBetter»

public class BetterBaseClass
{
    protected string state;

    public BetterBaseClass()
    {
        this.state = "BetterBaseClass";
        this.Initialize();
    }

    public void Initialize()
    {
        this.DisplayState();
    }

    public virtual void DisplayState()
    {
    }
}

public class DerivedFromBetter : BetterBaseClass
{
    public DerivedFromBetter()
    {
        this.state = "DerivedFromBetter";
    }

    public override void DisplayState()
    {
        Console.WriteLine(this.state);
    }
}
1 голос
/ 14 октября 2015

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

class Parent
{
    public Parent()
    {
        DoSomething();
    }
    protected virtual void DoSomething()
    {
    }
}

class Child : Parent
{
    private string foo = "HELLO";
    public Child() { /*Originally foo initialized here. Removed.*/ }
    protected override void DoSomething()
    {
        Console.WriteLine(foo.ToLower());
    }
}
1 голос
/ 23 сентября 2008

В данном конкретном случае есть разница между C ++ и C #. В C ++ объект не инициализирован, и поэтому небезопасно вызывать вирусную функцию внутри конструктора. В C # при создании объекта класса все его члены инициализируются нулями. Можно вызвать виртуальную функцию в конструкторе, но если вы можете получить доступ к элементам, которые по-прежнему равны нулю. Если вам не нужен доступ к членам, вполне безопасно вызывать виртуальную функцию в C #.

0 голосов
/ 14 декабря 2017

Я бы просто добавил метод Initialize () в базовый класс, а затем вызвал бы его из производных конструкторов. Этот метод вызовет любые виртуальные / абстрактные методы / свойства ПОСЛЕ того, как все конструкторы были выполнены:)

0 голосов
/ 22 мая 2014

Еще одна интересная вещь, которую я обнаружил, заключается в том, что ошибку ReSharper можно «устранить», выполнив что-то вроде ниже, что для меня глупо (однако, как упоминалось многими ранее, все еще не очень хорошая идея вызывать виртуальные проп / методы в т е р.

public class ConfigManager
{

   public virtual int MyPropOne { get; private set; }
   public virtual string MyPropTwo { get; private set; }

   public ConfigManager()
   {
    Setup();
   }

   private void Setup()
   {
    MyPropOne = 1;
    MyPropTwo = "test";
   }

}

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