在为属性赋值时,虚拟成员在构造函数中调用

com*_*cme 4 c# resharper virtual abstract-class properties

我有一个Abstract类和一个Derived类.抽象类定义了一个名为Message的抽象属性.在派生类中,通过重写abstract属性来实现该属性.派生类的构造函数采用字符串参数并将其分配给其Message属性.在Resharper中,此赋值会导致警告"构造函数中的虚拟成员调用".

AbstractClass有这个定义:

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

    protected AbstractClass() {}

    public abstract void PrintMessage();
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我确实找到了有关此警告的其他一些问题,但在这些情况下,实际上会调用一个方法.例如,在这个问题中,Matt Howels的答案包含一些示例代码.我在这里重复一遍以便于参考.

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

Matt没有描述警告会出现什么错误,但我假设它将在Parent构造函数中调用DoSomething.在这个例子中,我理解被调用的虚拟成员的含义.成员调用发生在基类中,其中仅存在虚方法.

然而,在我的情况下,我不明白为什么分配值Message会调用虚拟成员.Message属性的调用和实现都在派生类中定义.

虽然我可以通过制作Derived Class来摆脱错误sealed,但我想了解为什么这种情况会导致警告.

更新 根据Brett的回答,我尽力创建一个派生自DerivedClass的ChildClass,最终会导致异常.这就是我想出的:

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当然_foo,在Message setter中使用它有点傻,但重点是ReSharper没有看到这个类有什么问题.

但是,如果您尝试在这样的程序中使用ChildClass:

internal class Program {
    private static void Main() {
        var childClass = new ChildClass();
        childClass.PrintMessage();
    }
}
Run Code Online (Sandbox Code Playgroud)

创建ChildClass对象时,您将获得NullReferenceException.异常将由ChildClass'尝试使用抛出_foo.ToUpper()_foo尚未初始化.

Bre*_*ett 6

这是因为你的Message属性可以被覆盖class ChildClass : DerivedClass- 此时可以ChildClass从ctor中调用Message on中的代码DerivedClass,并且你的ChildClass实例可能没有被完全初始化.

这就是为什么让DerivedClass密封解决问题 - 它不能被继承.

  • 当你覆盖它时,你也可以将*method*标记为`sealed`,这意味着没有人可以在后代类中覆盖它 - 所以它实际上不再是虚拟的.那也应该删除错误. (3认同)