我从ReSharper收到一条关于从对象构造函数调用虚拟成员的警告。
为什么这是不该做的?
我从ReSharper收到一条关于从对象构造函数调用虚拟成员的警告。
为什么这是不该做的?
当前回答
该警告提醒您虚拟成员很可能在派生类上被重写。在这种情况下,父类对虚拟成员所做的任何操作都将通过重写子类来撤消或更改。看一下这个小例子
下面的父类试图将值设置为其构造函数上的虚拟成员。这将触发Re sharper警告,请参见代码:
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();
}
}
此处的子类重写父属性。如果此属性未标记为virtual,编译器将警告该属性隐藏父类上的属性,如果有意,建议您添加“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"
}
}
其他回答
警告的原因已经描述过了,但您将如何修复警告?您必须密封类或虚拟成员。
class B
{
protected virtual void Foo() { }
}
class A : B
{
public A()
{
Foo(); // warning here
}
}
您可以密封A类:
sealed class A : B
{
public A()
{
Foo(); // no warning
}
}
或者您可以密封方法Foo:
class A : B
{
public A()
{
Foo(); // no warning
}
protected sealed override void Foo()
{
base.Foo();
}
}
小心盲目地听从Resharper的建议,让课堂变得封闭!如果它是EF Code First中的模型,它将删除虚拟关键字,这将禁用其关系的延迟加载。
public **virtual** User User{ get; set; }
该警告提醒您虚拟成员很可能在派生类上被重写。在这种情况下,父类对虚拟成员所做的任何操作都将通过重写子类来撤消或更改。看一下这个小例子
下面的父类试图将值设置为其构造函数上的虚拟成员。这将触发Re sharper警告,请参见代码:
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();
}
}
此处的子类重写父属性。如果此属性未标记为virtual,编译器将警告该属性隐藏父类上的属性,如果有意,建议您添加“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"
}
}
我发现的另一件有趣的事情是,通过执行下面这样的操作可以“满足”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";
}
}
重要的一点是,解决这个问题的正确方法是什么?
正如Greg所解释的,这里的根本问题是基类构造函数会在构造派生类之前调用虚拟成员。
以下代码摘自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);
}
}