2010-12-09 13 views
5

派生クラスのプロパティをオーバーライドしています。 C#コンパイラはアクセス修飾子を変更することはできませんので、公開しておく必要があります。派生クラスでプロパティを読み取り専用にする

これを行うにはどうすればよいですか? set { }InvalidOperationExceptionを投げるだけでいいですか?

+0

だと思います。 – Noldorin

答えて

8

派生クラスにsetter throw InvalidOperationExceptionを設定すると、Liskov Subsitution Principleに違反します。本質的に、多型の価値を本質的に排除する基本クラスの型に文脈依存的にセッターを使用します。

派生クラスは、その基本クラスの契約を尊重する必要があります。セッターがすべての状況において適切でない場合、それは基本クラスに属しません。

これを回避する1つの方法は、階層を少し上にすることです。

class C1 { 
    public virtual int ReadOnlyProperty { get; } 
} 
class C2 { 
    public sealed override int ReadOnlyProperty { 
    get { return Property; } 
    } 
    public int Property { 
    get { ... } 
    set { ... } 
    } 
} 

あなたのタイプのあなたは、このシナリオでC1を継承することができ、残りはあなたが元の実装を隠し、基本実装を返すことができC2

+0

したがって、基本クラスに 'IsReadOnly'プロパティがあり、それ自身の例外がスローされる場合はOKですか?これは、 'NameObjectCollectionBase'>' NameValueCollection'> 'HttpValueCollection'継承がどのように動作するかです。これは' Page'の 'Request.Param'によって使用されます。私は原則の背後にある理由を理解していますが、 'NameObjectCollectionBase'の場合、それはあなたのステートメントと矛盾します:'セッターがすべての状況において適切でない場合、それは基本クラスに属しません。これを実装していないNETクラス(正当な理由で)。私はそれが私の場合には理にかなっていると思う。 –

+1

基本クラスを持つ@ネルソンは 'IsReadOnly'を持っています...最高の妥協点です。個人的に私はそれがひどいパターンだと思うし、BCLはコレクションが適切に変更可能なインターフェイスと読み取り専用のインターフェイス/タイプに分かれているとはるかに優れています。 – JaredPar

+0

それでは、具体的に説明します。私はASP.NETの 'ListView'から派生しています。それを 'MyCustomList'と呼ぶことにしましょう。 'ListView'のすべての機能やテンプレートが必要ですが、' MyCustomList'がデータを取得し、 'DataSource/Id'を割り当て、' DataSource/Id'が変更されないようにします。私は例外をスローすることは、自分自身のすべての 'ListView'機能を再実装するのと比べて、良い妥協であると思います。 –

3

から導き出すために切り替えることができに問題を抱えている:

class Foo 
{ 
    private string _someString; 
    public virtual string SomeString 
    { 
     get { return _someString; } 
     set { _someString = value; } 
    } 
} 

class Bar : Foo 
{ 
    public new string SomeString 
    { 
     get { return base.SomeString; } 
     private set { base.SomeString = value; } 
    } 
} 

namespace ConsoleApplication3 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Foo f = new Foo(); 
      f.SomeString = "whatever"; // works 
      Bar b = new Bar(); 
      b.SomeString = "Whatever"; // error 
     } 
    } 
} 

ただし、。 Jaredが言ったように、これは奇妙な状況です。あなたのセッターをプライベートにしたり、ベースクラスで保護するのはなぜですか?

+0

あなたが "Foo f2 = b; f.SomeString =" eeba geeba! ";"をあなたの例から実行すると、コンパイラはあなたにあなたを許可します。 – mjfgates

+0

@mjfgates:私はあなたが 'Foo f = new Bar();を意味すると思います。 f.SomeString = "blah" '?それが私がこのルートに行かなかった理由です。私は標準ASP.NET Webコントロールから派生しているので、基本クラスを変更することはできません。 –

+0

いいえ、私は私が書いたことを意味しました。その場合、SomeStringはBarの多型ではないため、Foo.SomeStringを呼び出しています.Fooの実装を隠しています。 –

0

基本クラスの公開メンバーをC#で非表示にすることはできません。誰かが派生クラスのインスタンスを取得し、それを基底クラスへの参照に埋め込み、そのようにプロパティにアクセスできるからです。

他のクラスのインターフェイスのほとんどではなくすべてを提供するクラスを作成する場合は、集計を使用する必要があります。 "基底クラス"を継承しないでください。 "派生"の値に値を1つ含めるだけで、公開する "基底クラス"機能の部分に独自のメンバー関数を提供します。これらの関数は、呼び出しを適切な「基本クラス」関数に転送することができます。

0

単にnewキーワードでプロパティを定義し、派生クラスにSetterメソッドを指定しないでください。これは基本クラスのプロパティも隠しますが、前述のmjfgatesはそれを基本クラスのインスタンスに割り当てることで基本クラスのプロパティにアクセスできます。それは多型です。

0

説明するパターンが正当なものである場合があります。たとえば、サブタイプMutableFooおよびImmutableFooから派生した抽象クラスMaybeMutableFooを持つと便利です。すべての3つのクラスには、IsMutableプロパティ、およびメソッドAsMutable(),AsNewMutable()およびAsImmutable()が含まれています。

IsMutableがtrueを返さない限り、契約者がセッターが機能しないことを明示的に指定する場合は、MaybeMutableFooが読み取りと書き込みのプロパティを公開することが完全に適切です。フィールドのオブジェクトがImmutableFooのインスタンスを保持しているオブジェクトは、そのオブジェクトに書かなければならない限り、そのインスタンスに完全に満足している可能性があります。AsMutable()で返されたオブジェクトでフィールドを置き換えてから使用します。変更可能なfoo(変更されたことは分かっているでしょう。MaybeMutableFooにセッターを含めると、いったん変更可能なインスタンスを参照するようになったら、フィールドで将来のタイプキャストを行う必要がなくなります。

このようなパターンを可能にする最良の方法は、仮想プロパティまたは抽象プロパティを実装するのを避け、代わりにゲッターとセッターが仮想メソッドまたは抽象メソッドに連動する非仮想プロパティを実装することです。一方は、ベースクラス

public class MaybeMutableFoo 
{ 
    public string Foo {get {return Foo_get();} set {Foo_set(value);} 
    protected abstract string Foo_get(); 
    protected abstract void Foo_set(string value}; 
} 

を有する場合、派生クラスImmutableFooを宣言してもよい:

new public string Foo {get {return Foo_get();} 

を作るために、そのFooプロパティ読み取り専用抽象Foo_get()ためのオーバーライドをコードする能力を妨害することなく、かつFoo_set()方法。読み取り専用置換Fooは、プロパティgetの動作を変更しないことに注意してください。読み取り専用バージョンは、ベースクラスのプロパティと同じベースクラスのメソッドにチェーンされます。このようにすると、プロパティゲッターを変更するためのパッチポイントとセッターを変更するためのパッチポイントが1つずつ存在し、プロパティ自体が再定義されてもそれらのパッチポイントは変更されません。

1

私は私、この状況より良い解決策は、私たちは、私が思うより多くのコンテキストを必要とする新しいキーワード

public class Aclass {   
    public int ReadOnly { get; set; } 
} 

public class Bclass : Aclass {   
    public new int ReadOnly { get; private set; } 
} 
+0

新しいキーワードを使用すると、ベースのgetおよびsetアクセサを隠すことができます – Dimitrios