构造函数中的虚拟成员调用 - 为什么一个好,另一个不行?

时间:2011-10-07 11:16:51

标签: c#

使用下面的代码,Resharper会在构造函数中发出'虚拟成员调用'警告:

public class UserDetailViewModel : Screen
{
    public UserDetailViewModel()
    {
        // DisplayName is a virtual member of Screen
        DisplayName = "User Details"; 
    }
}

然而,如果我更改代码,就像这样,警告就会消失:

public class UserDetailViewModel : Screen
{
    public UserDetailViewModel()
    {
        SetName();
    }

    private void SetName()
    {
        DisplayName = "User Details"; 
    }
}

为什么一个人发出警告而另一个没有?第二种方式是否在某种程度上是正确的,还是仅仅超出了ReSharper可以检测到的潜在危险的极限?

3 个答案:

答案 0 :(得分:13)

这只是ReSharper的限制。它应用了一堆启发式方法来查找它可以警告的代码,但它找不到所有内容。这样做需要解决暂停问题。这不可行。

这里的教训非常简单:
没有警告并不意味着没有什么值得警告的。

答案 1 :(得分:3)

在Caliburn.Micro中,通常通过覆盖OnInitialize()方法来设置DisplayName,例如。

    protected override void OnInitialize()
    {
        DisplayName = "User Details";
        base.OnInitialize();
    }

OnInitialize()仅被调用一次,并且您不再在Resharper中发出警告。另请参阅here

答案 2 :(得分:1)

由于在以下情况下可能出现的问题而发出警告

  • 派生类型UserDetailViewModel的对象,说“ConcreteModel”正在构建

  • ConcreteModel会覆盖Screen.DisplayName属性。在属性的set方法中,它取决于已构造完成的ConcreteModel,比如它访问在构造函数中初始化的另一个成员。

在这种情况下,上面的代码将引发异常。

解决此问题的正确方法是在DisplayName中声明sealedUserDetailViewModel。现在您可以放心地忽略警告。

以下示例演示了它。取消注释Der中的行会导致编译错误。

class Base
{
    public virtual string DisplayName { get; set; }
}

class Der : Base
{
    public Der()
    {
        //  ok to ignore virtual member access here
        DisplayName = "Der";
    }
    public override sealed string DisplayName { get; set; }
}

class Leaf : Der
{ 
    private string _displayName;
    public Leaf()
    {
        _displayName = "default";
    }
    //override public string DisplayName
    //{
    //    get { return _displayName; }
    //    set { if (!_displayName.Equals(value)) _displayName = value; }
    //}
}