Вызов виртуального члена в конструкторе при присваивании значения свойству - PullRequest
4 голосов
/ 07 января 2011

У меня есть класс Abstract и класс Derived.Абстрактный класс определяет абстрактное свойство с именем Message.В производном классе свойство реализуется путем переопределения абстрактного свойства.Конструктор производного класса принимает строковый аргумент и присваивает его свойству Message.В Resharper это назначение приводит к предупреждению «Вызов виртуального члена в конструкторе».

AbstractClass имеет следующее определение:

public abstract class AbstractClass {
    public abstract string Message { get; set; }

    protected AbstractClass() {}

    public abstract void PrintMessage();
}

И DerivedClass выглядит следующим образом:

using System;

public class DerivedClass : AbstractClass {
    private string _message;

    public override string Message {
        get { return _message; }
        set { _message = value; }
    }

    public DerivedClass(string message) {
        Message = message; // Warning: Virtual member call in a constructor
    }

    public DerivedClass() : this("Default DerivedClass message") {}

    public override void PrintMessage() {
        Console.WriteLine("DerivedClass PrintMessage(): " + Message);
    }
}

Я нашел несколько других вопросов об этом предупреждении, но в этих ситуациях есть реальный вызов метода.Например, в этом вопросе ответ Мэтта Хауэлса содержит пример кода.Я повторю это здесь для простоты справки.

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

class Child : Parent {
    private string foo;
    public Child() { foo = "HELLO"; }
    protected override void DoSomething() {
        Console.WriteLine(foo.ToLower());
    }
}

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

В моей ситуации, однако, я не понимаю, почему присвоение значения Message будет вызовом виртуального члена.И вызов, и реализация свойства Message определены в производном классе.

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

Обновление Основываясь на ответе Бретта, я приложил все усилия, чтобы создать ChildClass, производный от DerivedClass, который в конечном итоге приведет к исключению.Вот что я придумал:

using System;

public class ChildClass : DerivedClass {
    private readonly string _foo;

    public ChildClass() : base("Default ChildClass Message") {
        _foo = "ChildClass foo";
    }

    public override string Message {
        get { return base.Message; }
        set {
            base.Message = value;
            Console.WriteLine(_foo.ToUpper() + " received " + value);
        }
    }
}

Конечно, использовать _foo в установщике сообщений немного глупо, но дело в том, что ReSharper не видит ничего плохого в этом классе.

Если, однако, вы попытаетесь использовать ChildClass в программе, подобной этой:

internal class Program {
    private static void Main() {
        var childClass = new ChildClass();
        childClass.PrintMessage();
    }
}

Вы получите исключение NullReferenceException при создании объекта ChildClass.Исключение будет вызвано попыткой ChildClass использовать _foo.ToUpper(), так как _foo еще не инициализирован.

1 Ответ

6 голосов
/ 07 января 2011

Это потому, что ваше свойство Message может быть переопределено на class ChildClass : DerivedClass - в этот момент можно вызвать код в сообщении на ChildClass из ctor в DerivedClass, и ваш экземпляр ChildClass может быть не полностью инициализирован.

Вот почему закрытие DerivedClass решает проблему - она ​​не может быть унаследована.

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