开发者

Virtual member call in constructor - why is one okay and the other not?

With the code below, Resharper raises a 'virtual member call in constructor' warning:

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

Whereas if I change the code so it's like this, the warning disappears:

public class UserDetailViewModel : Screen
{
    public UserDetailViewMo开发者_开发知识库del()
    {
        SetName();
    }

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

Why does one raise a warning and the other not? Is the second way somehow correct, or is it just beyond the limit of what ReSharper can detect as potentially dangerous?


It's just a limitation of ReSharper. It applies a bunch of heuristics to find code that it can warn about, but it won't find everything. Doing so would require solving the halting problem. It's not doable.

The lesson here is quite simple:
the absence of a warning does not mean that there is nothing to warn about.


In Caliburn.Micro, it's common to set the DisplayName by overriding the OnInitialize() method, e.g.

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

OnInitialize() is only called once, and you do not raise a warning in Resharper anymore. See also here.


The warning is given because of the problem that can arise in the following situation

  • An object of a derived type of UserDetailViewModel, say "ConcreteModel` is being constructed

  • ConcreteModel overrides Screen.DisplayName property. In the property's set method it depends on the ConcreteModel constructed having completed, say it accesses another member that is initialized in the constructor.

In this case, the above code will throw an exception.

The right way to solve this is by declaring DisplayName to be sealed in UserDetailViewModel. Now you can feel confident that it is ok to ignore the warning.

The following example demonstrates it. Uncommenting the lines in Der causes compilation error.

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; }
    //}
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜