簡體   English   中英

為什么不在構造函數中調用可覆蓋的方法?

[英]Why do not call overridable methods in constructors?

這是一個過於簡單的例子,但我有一些現實生活中的代碼在概念上做同樣的事情(試圖驗證派生類的“set”訪問器方法的值),並且Analyzer給了我“不要在構造函數中調用可覆蓋的方法”。 我試圖找出是否應該更改我的代碼,或忽略警告。 我想不出任何理由我應該注意這個警告。

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();
        }
    }
}

正如TS所說(謝謝TS),在實例化派生類型時,將始終調用基礎構造函數。 如果你像我在問題中所做的那樣,衍生構造函數沒有明確指定要使用哪個基礎構造函數,那么使用無參數基礎構造函數。

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
    // ...

答案確實是,這可能會導致意外行為。

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將在您設置虛擬成員之前執行其構造函數。

我想補充說,構造函數中的調用和覆蓋方法可能會使程序處於不一致狀態。 如果您的方法拋出異常會發生什么? 那么你的對象永遠不會被構造出來。 在構造函數中捕獲這些異常並不是一個好習慣。

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

您可以從Windows窗體中學到的一個教訓是,設計器具有從構造函數調用的InitializeComponents。

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

InitializeComponent由設計者生成。 不要修改它,因為更改設計器屬性時更改將丟失。 InitializeComponent的目的僅僅是讓設計者將所有代碼設置為設置所有屬性,並在繪制設計器表面時將其讀取,以便呈現相關的組件設置

如果InitializeComponent是覆蓋方法怎么辦? 然后你可以修改它,最后,如果你的更改是錯誤的,整個表單可能處於不一致的狀態並打破基類的邏輯

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM