2017-02-06 51 views
1

我有一種情況,我想讓普通屬性在具有默認值的派生類中只讀。 我使用關鍵字以下列方式目的:將BaseClass屬性更改爲DerivedClass中的只讀屬性並覆蓋

public abstract class BaseClass 
{ 
    public virtual string SomeInfo { get; set; } 
} 
public class DerivedClass1 : BaseClass 
{ 
    public new string SomeInfo => "ChildInfo1"; // C# 6.0 equivalent of { get { return "ChildInfo1"; } } 
} 

它工作正常,並new DerivedClass1().SomeInfo不能被分配到 - 它是隻讀的。我知道,一個可以通過基類訪問:

BaseClass b1 = new DerivedClass1(); 
b1.SomeInfo = "ChildInfo1 changed"; 

我只想無法用戶意外更改,並通過基類,那將是目的,在這種情況下,它acceptible。

但是,如果派生類會是這樣的:

public class DerivedClass2 : BaseClass 
{ 
    public override string SomeInfo => "ChildInfo2"; 
} 

那麼這個屬性將可以使用,您可以更改貌似,但它不會改變的,我想知道爲什麼?

var d2 = new DerivedClass2(); 
d2.SomeInfo = "ChildInfo2 changed"; 
Console.WriteLine(d2.SomeInfo); // output: ChildInfo2 

UPDATE:
我已經添加了新的答案作爲第三個選擇,可能是最好的。

+1

*「我只想無法用戶意外更改,並通過基類,那將是目的,在這種情況下,它acceptible」 *如果程序員調用一個接受'BaseClass'參數的方法,程序員不知道該方法改變'SomeInfo'嗎?無論如何,你提出的建議違反了[Liskov替代原則](https://en.wikipedia.org/wiki/Liskov_substitution_principle),應該被懷疑。 –

+0

我在想這可能違反了LSP。 有沒有其他(更好)的方式來實現這一點,而不違反它? – borisdj

+0

那麼,你可以創建一個只讀的接口並傳遞它,但是當然這需要修改現有的方法,所以它可能不是一個選項。 –

回答

0

你應該避免在這種情況下使用「新」。 定義在派生類中,而不是在這樣一個只讀屬性:

public override string SomeInfo { get { return "ChildInfo"; } } 

,您仍然可以設置您的屬性,但它始終會返回默認值「兒童信息」。

+0

不一樣嗎? 這給出警告,它隱藏了繼承的成員,並建議添加新的,或覆蓋,如果這是有意的。 添加新只是明確確認隱藏但行爲是一樣的嗎? 因此LSP仍然受到侵犯。 – borisdj

+0

@Boris,你是對的。我忘了寫「覆蓋」。現在修復。是的,在這種情況下,使用「新」或「覆蓋」得到相同的結果。但是,如您所知,使用「新」會違反LSP。 – jacktric

+0

「新」和「覆蓋」是不一樣的。 您的初始文章沒有這兩個關鍵字,隱含意味着隱藏,所以「新」與無。 「覆蓋」,因爲你現在已經更新,就像我的第二個選項,在這種情況下,你可以訪問setter,但getter忽略它,所以這是違反直覺的。你認爲你已經改變了財產,但你沒有。 這就是爲什麼第三種選擇,我補充說作爲另一個答案,是最好的。 – borisdj

1

在你的基類,你有

public virtual string SomeInfo { get; set; } 

它是一個不錯的定義:如果你有一個Expression健全屬性來覆蓋它,你與

覆蓋,獲取屬性

private string _someInfo; 
public string SomeInfo 
{ 
    get {return _someInfo;} 
    set {_someInfo = value;} 
} 

public string SomeInfo 
{ 
    get {return "ChildInfo2";} 
} 

但是你不重寫set屬性,所以你仍然可以設置私有變量,但它不會改變其他任何東西。

如果你看看你的第一個例子:

BaseClass b1 = new DerivedClass1(); 
b1.SomeInfo = "ChildInfo1 changed"; 

碰巧確切同樣的事情,你可以設置屬性,因爲基類確實有setter和設置爲私有變量,但如果你試圖產值SomeProperty,你看,它沒有改變,仍然是「ChildInfo1」

+0

所以setter在它後面設置私有字段,但getter返回的是get方法的一部分,它實際上忽略了這個字段。 我現在明白了。 – borisdj

+0

@Boris是的,這個表達式的屬性只是意味着:「總是返回那個常量值」 –

0

我現在做了第三個選項,可能是最好的,在構造函數中設置默認值。 感謝@MatthewWatson提請注意LSP和@MaksimSimkin的解釋。

public abstract class BaseClass 
{ 
    public string SomeInfo { get; set; } 
} 

public class DerivedClass1 : BaseClass 
{ 
    public DerivedClass1() 
    { 
     base.SomeInfo = "ChildInfo1"; 
    } 
    public new string SomeInfo => base.SomeInfo; 
} 

這使得SomeInfo readOnly的從DerivedClass訪問時,如果通過BaseClass的訪問是編輯,並且定期更換適用,並且是來自兩類可見。所以沒有'未知'的行爲。

PS對於舊版本的C#這相當於:

public abstract class BaseClass 
{ 
    protected string _someInfo; 
    public string SomeInfo 
    { 
     get { return _someInfo; } 
     set { _someInfo = value; } 
    } 
} 

public class DerivedClass1 : BaseClass 
{ 
    public DerivedClass1() 
    { 
     _someInfo = "ChildInfo1"; 
    } 
    public new string SomeInfo 
    { 
     get { return _someInfo; } 
    } 
}