簡體   English   中英

從派生類隱藏屬性

[英]hiding property from derived class

Jon Skeet在他的視頻中提到了這個問題(雖然沒有提供答案)。

假設我們有一個名為Person的類,而Person類具有Name屬性

然后我們有另一個班,間諜。 當然,間諜是一個人,所以我們將從Person類派生。

public class Person
{
    public string Name { get; set; }
}

public class Spy : Person
{

}

我們不希望人們知道Spy的名字,所以我們希望這會給出一個編譯錯誤:

static void ReportSpy(Spy spy) {
  string name = spy.Name;
}

或者:

static void ReportSpy(Spy spy)
{
   Person spyAsPerson = spy;
   string name = spyAsPerson.Name;
}

我們怎么能防止這種事情發生呢?

在基本PersonPerson Name屬性為virtual 在派生的Spy類中,覆蓋該屬性並在getter中throw Exception

public class Person
{
    public virtual string Name { get; set; }
}

public class Spy : Person
{
    public override string Name
    {
        get
        {
            throw new Exception("You never ask for a spy's name!");
        }
        set
        {
            base.Name = value;
        }
    }
}

但是,我建議不要拋出異常

get
{
    return "**********";
}

因為,它打破了LSP (在另一個答案中提到)。 這意味着什么(只是一個例子),我總是可以這樣做

Person x = new Spy();

並將其傳遞給其他方法,可能就像

void RegisterForNextBallGame(Person p)
{
    playerList.Add(p.Name);
}

這種方法不知道體育場周圍的一些間諜漫游,在做一個簡單的誠實任務時崩潰!

編輯

為了說清楚,這個name=********** 仍然不是一個正確的解決方案 它只會從異常中拯救! 之后,人們可能會發現許多人在名稱為**********的代碼中走下來,這將導致后來的驚喜和其他問題。

更好的解決方案是更好的設計。 檢查內森的答案以獲得一些暗示。

如果作為一個人的一部分透露了你的名字:間諜不是人

讓間諜繼承人破壞了Liskov替換原則 :一個對象可以用它的子類型替換。

如果Spys沒有透露他們的名字,他們就不應該是您設計環境中的人物。 也許您可以設計不同的方式:

public interface IPerson
{
    void EatLunch();
}

public interface INameDisclosingPerson : IPerson
{
    string Name {get; set; }
}

public interface ISpy : IPerson
{
    void DrinkCocktail();
    Package MakeDrop();
}

現實世界中這種糟糕設計的一個例子是NetworkStream 它通過拋出NotSupportedException來實現Position屬性。 因此,為Stream編寫的代碼可能會在運行時為NetworkStream中斷。 我也不是這個的粉絲。 一個設計指南:錯誤的東西應該在編譯時破壞,從它們無法實現的接口繼承的對象是可怕的。

您可以使用new關鍵字隱藏基類方法或屬性:

public class Person
{
    public string Name { get; set; }
}

public class Spy : Person
{
    public new string Name
    {
        get { throw new InvalidOperationException(); }
    }
}

它不會給你編譯錯誤,如果你強制轉換,你仍然可以訪問基類Name屬性。

如果可以修改基類,請將屬性更改為虛擬。 然后你可以在derived和class中覆蓋它,並且即使使用多態調用也會拋出異常。

所有這些都將在運行時工作,在編譯時你無能為力。

正如其他人在答案中提到的那樣,這種設計打破了利斯科夫的替代原則,應該避免。

你不能。 如前所述,在訪問Spy的Name屬性時可能會拋出異常,但這仍然可以編譯。 並且,也已經提到過,這將打破Liskov替代原則,並且我想補充一點,即開放封閉原則。

暫無
暫無

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

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