为什么不在构造函数中调用可覆盖的方法?

时间:2014-01-01 00:56:40

标签: c# methods constructor override

这是一个过于简单的例子,但我有一些现实生活中的代码在概念上做同样的事情(尝试验证衍生类的值“设置”访问器方法),并且分析器给了我“不要调用可覆盖的方法构造函数“。我试图找出是否应该更改我的代码,或忽略警告。我想不出任何理由我应该注意这个警告。

public abstract class SimpleUrl
{
    protected string _url;
    public abstract string Url { get; set; }
    public SimpleUrl()
    { }
    public SimpleUrl(string Url)
    {
        this.Url = Url;
    }
}

public class HttpUrl : SimpleUrl
{
    public HttpUrl()
    { }
    public HttpUrl(string Url)
    {
        this.Url = Url;
    }
    public override string Url
    {
        get
        {
            return this._url;
        }
        set
        {
            if (value.StartsWith("http://"))
                this._url = value;
            else
                throw new ArgumentException();
        }
    }
}

3 个答案:

答案 0 :(得分:5)

作为T.S.说(谢谢T.S.),在实例化派生类型时,将始终调用基础构造函数。如果你像我在问题中所做的那样,衍生构造函数不显式地指定要使用哪个基础构造函数,那么使用无参数基础构造函数。

public HttpUrl()           // : base() is implied.

public HttpUrl(string Url) // : base() is still implied.  
                           // Parameterless. Not :base(Url)

所以这个问题的完整答案是:如果密封衍生类,基类中声明的抽象或虚方法只能在基类中可覆盖 。因此,要创建干净的代码,不要在基础构造函数中运行此行:

this.Url = Url;   // Don't do this in the base constructor

相反,您应该“密封”衍生类(或方法)。如下:

public abstract class SimpleUrl
{
    protected string _url;
    public abstract string Url { get; set; }
    // Optionally declare base constructors that do *not* call Url.set
    // Not sure why these constructors are optional in the base, but
    // required in the derivative classes.  But they are.
    public SimpleUrl()
    { }
}

public sealed class HttpUrl : SimpleUrl
{
    public HttpUrl()   // Not sure why this is required, but it is.
    { }
    public HttpUrl(string Url)
    {
        // Since HttpUrl is sealed, the Url set accessor is no longer
        // overridable, which makes the following line safe.
        this.Url = Url;
    }
    public override string Url
    {
        get
        {
            return this._url;
        }
        set
        {
            if (value.StartsWith("http://"))
                this._url = value;
            else
                throw new ArgumentException();
        }
    }
}

或者,如果您只是密封可覆盖的方法(使其不再被任何其他衍生类别覆盖),则不需要密封整个衍生类别。

public class HttpUrl : SimpleUrl
{
    // ...
    public override sealed string Url
    // ...

答案 1 :(得分:4)

答案实际上是,这可能导致意外行为。

http://msdn.microsoft.com/en-us/library/ms182331.aspx

您在代码中遗漏的内容:

public class HttpUrl : SimpleUrl
{
    public HttpUrl()**:base()**
    { }
    public HttpUrl(string Url)**:base(Url)**
    {
        this.Url = Url;
    }
.........
}
你现在看到了吗? 你不能没有构造函数你的基础暴露。然后,base将在您设置虚拟成员之前执行其构造函数。

答案 2 :(得分:0)

我想补充一点,构造函数中的调用和覆盖方法会使程序处于不一致状态。如果您的方法抛出异常会发生什么?那么你的对象永远不会被构造出来。在构造函数中捕获这些异常并不是一个好习惯。

ctor()
{
    method(); //throws an exception
}

您可以从Windows窗体中学到的一个教训是,设计器具有从构造函数调用的InitializeComponents。

public MyView: System.Windows.Forms.Form
{
   public MyView()
   {
      InitializeComponent();
   }
}
  

InitializeComponent由设计者生成。不要修改它   因为更改设计器属性时更改将丢失。   InitializeComponent的目的仅仅是让设计师放置所有   它的代码设置所有属性,以及它何时可以读取   绘制设计器表面,以呈现相关的   组件设置

如果InitializeComponent是覆盖方法怎么办?然后你可以修改它,最后,如果你的更改是错误的,整个表单可能处于不一致的状态并打破基类的逻辑