为属性赋值时在构造函数中调用虚拟成员
我有一个抽象类和一个派生类。抽象类定义了一个名为 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);
}
}
我确实发现了有关此警告的其他一些问题,但在这些情况下,存在对方法的实际调用。例如,在 这个问题中,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());
}
}
马特没有描述警告会出现什么错误,但我假设它将出现在父构造函数中调用 DoSomething 时。在这个例子中,我明白了虚拟成员被调用是什么意思。成员调用发生在基类中,基类中只存在虚方法。
然而,在我的情况下,我不明白为什么为 Message
赋值会调用虚拟成员。 Message 属性的调用和实现都在派生类中定义。
虽然我可以通过密封派生类来消除错误,但我想了解为什么这种情况会导致警告。
更新 根据 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);
}
}
}
当然,在 Message setter 中使用 _foo
有点愚蠢,但重点是 ReSharper 没有发现这个类有任何问题。
但是,如果您尝试在如下程序中使用 ChildClass:
internal class Program {
private static void Main() {
var childClass = new ChildClass();
childClass.PrintMessage();
}
}
创建 ChildClass 对象时,您将收到 NullReferenceException。由于 _foo
尚未初始化,ChildClass 尝试使用 _foo.ToUpper()
将引发异常。
I have an Abstract class and a Derived class. The abstract class defines an abstract property named Message. In the derived class, the property is implemented by overriding the abstract property. The constructor of the derived class takes a string argument and assigns it to its Message property. In Resharper, this assignment leads to a warning "Virtual member call in constructor".
The AbstractClass has this definition:
public abstract class AbstractClass {
public abstract string Message { get; set; }
protected AbstractClass() {}
public abstract void PrintMessage();
}
And the DerivedClass is as follows:
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);
}
}
I did find some other questions about this warning, but in those situations there is an actual call to a method. For instance, in this question, the answer by Matt Howels contains some sample code. I'll repeat it here for easy reference.
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());
}
}
Matt doesn't describe on what error the warning would appear, but I'm assuming it will be on the call to DoSomething in the Parent constructor. In this example, I understand what is meant by a virtual member being called. The member call occurs in the base class, in which only a virtual method exists.
In my situation however, I don't see why assigning a value to Message
would be calling a virtual member. Both the call to and the implementation of the Message property are defined in the derived class.
Although I can get rid of the error by making my Derived Class sealed
, I would like to understand why this situation is resulting in the warning.
Update
Based on Brett's answer, I did my best to create a ChildClass derived from the DerivedClass that will ultimately result in an exception. This is what I came up with:
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);
}
}
}
Off course it's a bit silly to be using _foo
in the Message setter, but the point is that ReSharper doesn't see anything wrong with this class.
If however, you try to use the ChildClass in a program like this:
internal class Program {
private static void Main() {
var childClass = new ChildClass();
childClass.PrintMessage();
}
}
You'll get a NullReferenceException when creating the ChildClass object. The exception will be thrown by the ChildClass' attempt to use _foo.ToUpper()
as _foo
isn't initialized yet.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这是因为您的 Message 属性可以被
class ChildClass : DerivedClass
覆盖 - 此时可以从DerivedClass
中的构造函数调用ChildClass
上的 Message 中的代码code>,并且您的ChildClass
实例可能未完全初始化。这就是为什么将
DerivedClass
密封可以解决问题 - 它无法被继承。It's because your Message property can be overridden by
class ChildClass : DerivedClass
- at which point it's possible to invoke code in Message onChildClass
from the ctor inDerivedClass
, and yourChildClass
instance may not be full initialised.That's why making your
DerivedClass
sealed solves the problem - it cannot be inherited.