ゲッターのみのプロパティをオーバーライドしてセッターを追加できないのはなぜですか?[閉まっている]


141

次のC#コードが許可されないのはなぜですか。

public abstract class BaseClass
{
    public abstract int Bar { get;}
}

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get { return 0; }
        set {}
    }
}

CS0546 'ConcreteClass.Bar.set': 'BaseClass.Bar'にオーバーライド可能なセットアクセサーがないため、オーバーライドできません


9
StackOverflowは、Microsoftに代わって質問に回答する機能が制限されています。質問の言い換えを検討してください。
ジェイバズジ2008

1
ちなみに、.netのこの厄介な動作に対する簡単な解決策はFoo、保護された抽象GetFooメソッドをラップする以外何もしない非仮想の読み取り専用プロパティを持つことです。抽象派生型は、前述のGetFooメソッドと抽象SetFooメソッドをラップする以外に何もしない非仮想の読み取り/書き込みプロパティでプロパティをシャドウできます。具体的な派生型は上記を実行できますが、GetFooおよびの定義を提供しSetFooます。
スーパーキャット2012年

1
火に燃料を追加するためだけに、C ++ / CLIはこれを許可します。
ymett 2016年

1
プロパティをオーバーライドするときにアクセサーを追加する機能は、C#の将来のバージョンの変更として提案されています(github.com/dotnet/roslyn/issues/9482)。
Brandon Bonds

1
率直に言って、この質問は、次のパターンを使用する既存のライブラリタイプを拡張する必要がある場合によく出されます:読み取り専用の基本クラス、派生した可変クラス。ライブラリはすでに不正なパターン(WPFなど)を使用しているため、この不正なパターンを回避する必要が生じます。講義によって、1日の終わりに回避策を実装する必要がなくなります。
rwong 2017年

回答:


-4

Baseclassの作成者は、Barが読み取り専用プロパティでなければならないことを明示的に宣言しているためです。この契約を破って読み書き可能にすることは、派生にとって意味がありません。

私はこれに関してマイクロソフトと一緒です。
私がBaseclassの派生に対してコーディングするように言われた新しいプログラマーであるとしましょう。私はBarに書き込むことができないと想定する何かを書きます(Baseclassはそれがget onlyプロパティであると明示的に述べているため)。今あなたの派生で、私のコードは壊れるかもしれません。例えば

public class BarProvider
{ BaseClass _source;
  Bar _currentBar;

  public void setSource(BaseClass b)
  {
    _source = b;
    _currentBar = b.Bar;
  }

  public Bar getBar()
  { return _currentBar;  }
}

BarはBaseClassインターフェースに従って設定できないため、BarProviderはキャッシングを安全に行うことを想定しています-Barは変更できないためです。ただし、派生で設定が可能である場合、誰かが_sourceオブジェクトのBarプロパティを外部で変更した場合、このクラスは古い値を提供している可能性があります。ポイントは「オープンであること、卑劣なことや意外な人を避けて

更新Ilya Ryzhenkovが「なぜ同じルールでインターフェイスを再生しないのですか?」 うーん..私が考えると、これはどんどん悪化します。
インターフェースとは、「実装にBarという名前の読み取りプロパティがあることを期待する」という規約です。個人的には、インターフェースを見た場合に、読み取り専用であると想定する可能性ははるかに低くなります。インターフェイスでget-onlyプロパティが表示されると、「どの実装でもこの属性Barが公開される」としてそれを読みます...「Bar is a read-only property」としてクリックする基本クラスで。もちろん、技術的には契約を破っているわけではありません。だからあなたはある意味で正しいのです。私は「誤解をできるだけ起こさないようにする」と言って締めくくります。


94
私はまだ確信していません。明示的なセッターがない場合でも、これはプロパティが常に同じオブジェクトを返すことを保証するものではありません。他のメソッドによって変更される可能性があります。
ripper234 2008

34
それでは、インターフェースについても同じではないでしょうか?インターフェースに読み取り専用プロパティを、型の実装に読み取り/書き込みを設定できます。
Ilya Ryzhenkov 2008

49
私は同意しません。基本クラスはプロパティをセッターなしとして宣言しただけです。これは、プロパティが読み取り専用であることとは異なります。「読み取り専用」は、あなたがそれに割り当てる意味だけです。C#に組み込まれた関連する保証はまったくありません。毎回変更されるget-onlyプロパティ、またはセッターがをスローするget / setプロパティを使用できInvalidOperationException("This instance is read-only")ます。
ローマン・スターコフ2010

21
メソッドとしてゲッターとセッターを見ると、なぜそれが「契約違反」でなければならないのか、新しいメソッドを追加する以上にわかりません。基本クラスのコントラクトは、存在しない機能とは関係なく、存在する機能とのみ関係あります。
Dave Cousineau

37
-1私はこの回答に同意しませんが、それは誤解を招きます。基本クラスは、どのクラスも従う必要がある動作を定義しているため(Liskovの置換原則を参照)、動作を追加することを制限しません(そして制限してはなりません)。基本クラスは振る舞いが何である必要があるかを定義するだけでよく、振る舞いを「してはいけない」(すなわち「具体的なレベルで書き込み可能であってはならない」)ことを指定することはできません(すべきではありません)。
Marcel Valdez Orozco 2013年

39

主な理由は、構文が明示的すぎて他の方法で機能しないためです。このコード:

public override int MyProperty { get { ... } set { ... } }

との両方がオーバーライドされgetsetいることは非常に明確です。set基本クラスにはないので、コンパイラーは不平を言います。基本クラスで定義されていないメソッドをオーバーライドできないのと同じように、セッターもオーバーライドできません。

コンパイラは意図を推測し、オーバーライド可能なメソッド(つまり、この場合はゲッター)にのみオーバーライドを適用する必要があると言うかもしれませんが、これはC#設計原則の1つに反します-コンパイラは意図を推測してはなりません、知らないうちに間違って推測する可能性があるため。

次の構文はうまくいくと思いますが、Eric Lippertが言い続けているように、このようなマイナーな機能を実装することも依然として大きな労力を費やしています...

public int MyProperty
{
    override get { ... }
    set { ... }
}

または、自動実装されたプロパティの場合、

public int MyProperty { override get; set; }

19

今日も同じ問題に出くわしましたが、これを望む理由は非常にあると思います。

最初に、get-onlyプロパティを持つことは必ずしも読み取り専用に変換されるわけではないことを主張したいと思います。私はそれを「このインターフェイス/抽象からこの値を取得できる」と解釈します。それは、そのインターフェイス/抽象クラスの一部の実装がこの値を明示的に設定するためにユーザー/プログラムを必要としないことを意味しません。抽象クラスは、必要な機能の一部を実装する目的を果たします。継承されたクラスがコントラクトに違反せずにセッターを追加できなかった理由はまったくわかりません。

以下は、今日必要なものの簡単な例です。これを回避するためだけに、インターフェースにセッターを追加する必要がありました。たとえば、SetPropメソッドを追加せずにsetterを追加する理由は、Propのシリアル化にインターフェイスの特定の実装がDataContract / DataMemberを使用していたためです。シリアライゼーションの。

interface ITest
{
    // Other stuff
    string Prop { get; }
}

// Implements other stuff
abstract class ATest : ITest
{
    abstract public string Prop { get; }
}

// This implementation of ITest needs the user to set the value of Prop
class BTest : ATest
{
    string foo = "BTest";
    public override string Prop
    {
        get { return foo; }
        set { foo = value; } // Not allowed. 'BTest.Prop.set': cannot override because 'ATest.Prop' does not have an overridable set accessor
    }
}

// This implementation of ITest generates the value for Prop itself
class CTest : ATest
{
    string foo = "CTest";
    public override string Prop
    {
        get { return foo; }
        // set; // Not needed
    }
}

これは単なる「私の2セント」の投稿であることはわかっていますが、元のポスターを感じ、これが良いことだと合理化しようとするのは奇妙に思えます。特に、インターフェース。

また、オーバーライドの代わりにnewを使用することについての言及はここでは適用されません。単に機能しないだけでなく、機能しても、必要な結果、つまり、インターフェースで記述されている仮想ゲッターが提供されません。


2
または、私の場合、{get; プライベートセット; }。セットは非公開のままであり、派生クラスのみに関係しているので、私はいかなる契約にも違反していません。
Machtyn

16

それが可能だ

TL; DR - あなたがしたい場合は、セッターとのget-のみのメソッドをオーバーライドすることができます。基本的には次のとおりです。

  1. 同じ名前を使用してnewa getとaの両方を持つプロパティを作成しますset

  2. 他に何もしないget場合、派生クラスがその基本型を通じて呼び出されると、古いメソッドが呼び出されます。これを修正するには、古いメソッドをabstract使用overridegetて新しいgetメソッドの結果を返すように強制する中間層を追加します。

これにより、基本定義にプロパティがなくても、get/ setでプロパティをオーバーライドできます。

おまけとして、必要に応じて戻り値の型を変更することもできます。

  • 基本定義がget-onlyの場合、より派生した戻り値の型を使用できます。

  • 基本定義がset-onlyの場合、あまり派生していない戻り値の型を使用できます。

  • 基本定義がすでにget/ setであった場合、次のようになります。

    • あなたはより多くの派生戻り値の型を使用することができた場合、あなたがそれを作るset-only。

    • あなたはあまり由来の戻り値の型を使用することができた場合、あなたがそれを作るget-only。

どの場合でも、必要に応じて同じ戻り値の型を維持できます。以下の例では、簡単にするために同じ戻り値の型を使用しています。

状況:既存の- getのみのプロパティ

変更できないクラス構造があります。たぶん、それはたった1つのクラスか、既存の継承ツリーです。いずれにせよ、setプロパティにメソッドを追加したいのですが、できません。

public abstract class A                     // Pre-existing class; can't modify
{
    public abstract int X { get; }          // You want a setter, but can't add it.
}
public class B : A                          // Pre-existing class; can't modify
{
    public override int X { get { return 0; } }
}

問題:することはできません-onlyと/overridegetgetset

/ プロパティを使用overrideしたいのですが、コンパイルされません。getset

public class C : B
{
    private int _x;
    public override int X
    {
        get { return _x; }
        set { _x = value; }   //  Won't compile
    }
}

解決策:abstract中間層を使用する

/ プロパティを直接override使用することはできませんが、次のことができますgetset

  1. 同じ名前でnew get/ setプロパティを作成します。

  2. override一貫性を確保するためgetに、新しいgetメソッドへのアクセサを備えた古いメソッド。

したがって、最初にabstract中間層を記述します。

public abstract class C : B
{
    //  Seal off the old getter.  From now on, its only job
    //  is to alias the new getter in the base classes.
    public sealed override int X { get { return this.XGetter; }  }
    protected abstract int XGetter { get; }
}

次に、以前にコンパイルしないクラスを記述します。あなたが実際にしていないので、この時間をコンパイルしますoverride「INGのgetのみのプロパティを。代わりに、newキーワードを使用して置き換えます。

public class D : C
{
    private int _x;
    public new virtual int X { get { return this._x; } set { this._x = value; } }

    //  Ensure base classes (A,B,C) use the new get method.
    protected sealed override int XGetter { get { return this.X; } }
}

結果:すべてが機能します!

明らかに、これは意図したとおりに動作しDます。

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

test.X = 7;
Print(test.X);      // Prints "7", as intended.

やなどD、基本クラスの1つとして表示した場合でも、すべてが意図したとおりに機能します。しかし、それが機能する理由は少し明白ではないかもしれません。AB

var test = new D() as B;
//test.X = 7;       // This won't compile, because test looks like a B,
                    // and B still doesn't provide a visible setter.

ただし、の基本クラスの定義は、get最終的には派生クラスのの定義によってオーバーライドされるgetため、完全に一貫しています。

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

var baseTest = test as A;
Print(test.X);      // Prints "7", as intended.

討論

このメソッドを使用するとsetget-onlyプロパティにメソッドを追加できます。また、次のようなことにも使用できます。

  1. 任意のプロパティを変更しget、-only set-only、またはget-と- setに関係なく、それは、基本クラスにあったものを、プロパティ。

  2. 派生クラスのメソッドの戻り型を変更します。

主な欠点は、行うべきコーディングが多いこととabstract class、継承ツリーに余分なものがあることです。これらは中間層でコピー/貼り付けする必要があるため、パラメーターを受け取るコンストラクターではこれは少し面倒です。


これに独自の質問をしてください:stackoverflow.com/questions/22210971
Nat

素敵なアプローチ(+1)。ただし、これがエンティティフレームワークでスムーズに機能するかどうかは疑問です。
Mike de Klerk 2017

9

派生型でゲッターをオーバーライドできないことはアンチパターンであることに同意します。読み取り専用は、純粋な機能のコントラクトではなく、実装の欠如を指定します(トップ投票の回答によって暗示されます)。

同じ誤解が助長されたため、またはおそらく文法が単純化されたために、Microsoftにこの制限があったと思います。ただし、スコープを適用して個別に取得または設定できるようになったため、おそらくオーバーライドも可能になると期待できます。

上位投票の回答に示されている誤解は、読み取り専用プロパティは、どういうわけか、読み取り/書き込みプロパティよりも「純粋」である必要があるということです。フレームワークの多くの一般的な読み取り専用プロパティを見てください。値は定数/純粋に機能的ではありません。たとえば、DateTime.Nowは読み取り専用ですが、純粋な関数値以外のものです。読み取り専用プロパティの値を「キャッシュ」して、次回同じ値を返すと想定すると、危険です。

いずれにせよ、私はこの制限を克服するために次の戦略の1つを使用しました。どちらも完全ではありませんが、この言語の欠陥を超えて足を踏み入れることができます。

   class BaseType
   {
      public virtual T LastRequest { get {...} }
   }

   class DerivedTypeStrategy1
   {
      /// get or set the value returned by the LastRequest property.
      public bool T LastRequestValue { get; set; }

      public override T LastRequest { get { return LastRequestValue; } }
   }

   class DerivedTypeStrategy2
   {
      /// set the value returned by the LastRequest property.
      public bool SetLastRequest( T value ) { this._x = value; }

      public override T LastRequest { get { return _x; } }

      private bool _x;
   }

DateTime.Nowは、副作用がないという点で純粋に機能します(実行に時間がかかるという事実は、おそらく副作用と呼ばれる可能性がありますが、実行時間は、少なくとも制限付きで短い場合は考慮されないため)他のメソッドの副作用、私はそれがここでも1つであるとは考えません)。
スーパーキャット2012年

1
こんにちはスーパーキャット。関数が純粋になるのを妨げる外部から観察可能な副作用については正しいですが、他の制約は、純粋な関数の結果値はパラメーターにのみ依存する必要があるということです。したがって、関数としての純粋な静的プロパティは、不変かつ定数でなければなりません。洗練されたコンパイラーは、最初の呼び出しによって返された結果をパラメーターなしで純粋な関数への後続のすべての呼び出しに置き換えることができます。
T.Tobler 2013年

私の用語が不正確だったのはあなたの言うとおりですが、メソッドではなく読み取り専用プロパティであるものの適切さは、純粋さではなく副作用の欠如に依存していると思います。
スーパーキャット2013年

2

新しいプロパティを作成することで問題を回避できます。

public new int Bar 
{            
    get { return 0; }
    set {}        
}

int IBase.Bar { 
  get { return Bar; }
}

4
ただし、これは、基本クラスから継承するときではなく、インターフェイスを実装するときにのみ実行できます。別の名前を選択する必要があります。
スピック

サンプルクラスがIBase(パブリックインターフェイスIBase {int Bar {get;}}のようなもの)を実装すると想定します。それ以外の場合、クラスの実装では、int IBase.Bar {...}は許可されません。次に、getとsetの両方を使用して通常のプロパティBarを簡単に作成できます。eventhoインターフェースのBarプロパティは、セットを必要としません。
イブラヒムベンサラ2015年

1

私はあなたのすべてのポイントを理解できますが、その場合、C#3.0の自動プロパティは事実上役に立たなくなります。

あなたはそのようなことは何もできません:

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get;
        private set;
    }
}

IMO、C#はそのようなシナリオを制限するべきではありません。それを適切に使用するのは開発者の責任です。


あなたの言い分がわかりません。C#がセッターの追加を許可する必要があることに同意しますか、それともこの質問に回答した他のほとんどの人と一緒ですか?
ripper234 2008

2
私が言ったように、IMO、C#はセッターを追加できるようにする必要があります。それを適切に使用するのは開発者の責任です。
Thomas Danecker

1

問題は、何らかの理由で、Microsoftが3つの異なるタイプのプロパティ(読み取り専用、書き込み専用、読み書き可能)があるべきであると決定したことです。プロパティは、同じように宣言されたプロパティによってのみオーバーライドできます。やりたいことを行うには、同じ名前と署名を持つ2つのプロパティを作成する必要があります。1つは読み取り専用で、もう1つは読み書き可能です。

個人的には、プロパティっぽい構文が「get」メソッドと「set」メソッドを呼び出す構文糖として使用できることを除いて、「プロパティ」の概念全体が廃止されることを望みます。これにより、「セットの追加」オプションが容易になるだけでなく、「取得」で「セット」とは異なるタイプを返すことができるようになります。このような機能はそれほど頻繁に使用されることはありませんが、「セット」がラッパーまたは実際のデータを受け入れることができる一方で、「取得」メソッドがラッパーオブジェクトを返すと便利な場合があります。


0

Reflectionを使用してこれを実現するための回避策を次に示します。

var UpdatedGiftItem = // object value to update;

foreach (var proInfo in UpdatedGiftItem.GetType().GetProperties())
{
    var updatedValue = proInfo.GetValue(UpdatedGiftItem, null);
    var targetpropInfo = this.GiftItem.GetType().GetProperty(proInfo.Name);
    targetpropInfo.SetValue(this.GiftItem, updatedValue,null);
}

このようにして、読み取り専用のプロパティにオブジェクト値を設定できます。ただし、すべてのシナリオで機能するとは限りません。


1
存在しないリフレクションを介してセッターを呼び出すことができますか?
またはMapper 2013

0

質問のタイトルを変更して、質問が抽象プロパティのオーバーライドのみに関するものであるか、または一般的にクラスのget-onlyプロパティのオーバーライドに関するものであるかのいずれかの詳細に変更する必要があります。


前者の場合(抽象プロパティをオーバーライドする)

そのコードは役に立たない。基本クラスだけでは、Get-Onlyプロパティ(おそらくインターフェイス)のオーバーライドを強制されていることを知らせてはなりません。基本クラスは、実装クラスからの特定の入力を必要とする可能性がある共通機能を提供します。したがって、共通の機能が抽象プロパティまたはメソッドを呼び出す場合があります。この場合、一般的な機能メソッドは、次のような抽象メソッドをオーバーライドするように求めているはずです。

public int GetBar(){}

しかし、それを制御できず、基本クラスの機能が独自のパブリックプロパティ(奇妙な)から読み取る場合は、次のようにします。

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    private int _bar;
    public override int Bar
    {
        get { return _bar; }
    }
    public void SetBar(int value)
    {
        _bar = value;
    }
}

(奇妙な)コメントを指摘したいのですが、クラスが独自のパブリックプロパティを使用せず、プライベート/保護フィールドが存在する場合はそれを使用するのがベストプラクティスだと思います。したがって、これはより良いパターンです:

public abstract class BaseClass {
    protected int _bar;
    public int Bar { get { return _bar; } }
    protected void DoBaseStuff()
    {
        SetBar();
        //Do something with _bar;
    }
    protected abstract void SetBar();
}

public class ConcreteClass : BaseClass {
    protected override void SetBar() { _bar = 5; }
}

後者の場合(クラスのget-onlyプロパティをオーバーライドする)

すべての非抽象プロパティにはセッターがあります。さもなければそれは役に立たず、あなたはそれを使うことを気にすべきではありません。Microsoftは、あなたが望むことをすることを許可する必要はありません。理由:セッターはなんらかの形で存在し、Veerryyが望むものを簡単に達成できます。

基底クラス、またはあなたが持つプロパティを読み取ることができる任意のクラス{get;}、持っているSOMEそのプロパティの暴露セッターのようなものを。メタデータは次のようになります。

public abstract class BaseClass
{
    public int Bar { get; }
}

しかし、実装には、複雑さの範囲の両端があります。

最小複合体:

public abstract class BaseClass
{
    private int _bar;
    public int Bar { 
        get{
            return _bar;
        }}
    public void SetBar(int value) { _bar = value; }
}

最も複雑:

public abstract class BaseClass
{
    private int _foo;
    private int _baz;
    private int _wtf;
    private int _kthx;
    private int _lawl;

    public int Bar
    {
        get { return _foo * _baz + _kthx; }
    }
    public bool TryDoSomethingBaz(MyEnum whatever, int input)
    {
        switch (whatever)
        {
            case MyEnum.lol:
                _baz = _lawl + input;
                return true;
            case MyEnum.wtf:
                _baz = _wtf * input;
                break;
        }
        return false;
    }
    public void TryBlowThingsUp(DateTime when)
    {
        //Some Crazy Madeup Code
        _kthx = DaysSinceEaster(when);
    }
    public int DaysSinceEaster(DateTime when)
    {
        return 2; //<-- calculations
    }
}
public enum MyEnum
{
    lol,
    wtf,
}

私の要点は、いずれにしても、セッターを公開しているということです。あなたの場合、int Bar基本クラスで処理したくない、それがどのように処理されているかを確認するためのアクセス権がない、または意志に反する実際の迅速な汚いコードを実行するタスクがあったため、オーバーライドすることができます。

後と元の両方で(結論)

ストーリーショート:Microsoftが何かを変更する必要はありません。実装クラスの設定方法を選択し、コンストラクターを無視して、基本クラスのすべてを使用するか、まったく使用しないかを選択できます。


0

それでも、C#6.0では、オーバーライドされたgetter-onlyプロパティに「readonly」セッターが自動的に追加されます。

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    public override int Bar { get; }

    public ConcreteClass(int bar)
    {
        Bar = bar;
    }
}

-1

それはカプセル化と実装の隠蔽の概念を壊してしまうからです。クラスを作成してそれを出荷し、クラスのコンシューマーが自分で最初にゲッターのみを提供するプロパティを設定できるようにする場合を考慮してください。これは、実装に依存できるクラスの不変条件を効果的に破壊します。


1
-1:セッターを使用しても、子孫が不変条件を自動的に解除することはありません。セッターは、とにかく子孫によってすでに変更可能なものだけを変更できます。
Roman Starkov

@RomanStarkovそれは本当です。しかし、この投稿の問題は次のとおりです。あなたが契約しない抽象化にセッターを追加できないのはなぜですか?あなたのコメントは反対の質問で適切でしょう。とにかく、子孫がプライベートフィールドを完全に制御しているのに、Microsoftが抽象化でReadOnlyプロパティを作成できるのはなぜですか?
Suamere 2017年

+1:この質問は、マイクロソフトが契約の解除を許可しない理由についてです。反対票を投じる理由は、Microsoftが契約に読み取り専用プロパティのみを含めることを許可する理由についてです。したがって、この答えは文脈上正しいです。
Suamere 2017年

@Suamere契約には、プロパティを変更できないことが記載されていません。読み取れるプロパティが存在するということだけです。セッターを追加しても、この契約は破られません。読み取り専用プロパティを作成する意図としてそれを解釈できますが、C#6では変更する方法がないことを保証できないため、考えているコントラクトが実際にはないことを保証します。インターフェースについて考えてみましょう。それはgettable(get-onlyではない)プロパティのコントラクトを提供することができます。このインターフェイスは、get + setプロパティで問題なく実装できます。
Roman Starkov

具体的な著者の観点からこれに取り組んでいます。彼は好きなようにそれを変えることができます。基本クラスの作成者の観点からアプローチしています->実装クラスの最終結果のコンシューマーとしてアプローチしています。基本クラスの作成者は、具体的なクラスの作成者がケースバイケースで処理する特別なメソッドを公開せずに、将来の具象クラスのコンシューマーがそのプロパティを変更できるようにしたくありません。基本クラスでは、そのプロパティは不変として扱われ、そのように公開するだけで通信されます。
Suamere 2017年

-1

これは不可能ではありません。プロパティで「新しい」キーワードを使用するだけです。例えば、

namespace {
    public class Base {
        private int _baseProperty = 0;

        public virtual int BaseProperty {
            get {
                return _baseProperty;
            }
        }

    }

    public class Test : Base {
        private int _testBaseProperty = 5;

        public new int BaseProperty {
            get {
                return _testBaseProperty;
            }
            set {
                _testBaseProperty = value;
            }
        }
    }
}

このアプローチは、この議論の両サイドを満たしているように見えます。「新規」を使用すると、基本クラスの実装とサブクラスの実装の間の契約が破られます。これは、クラスが複数のコントラクトを持つことができる場合に必要です(インターフェイスまたは基本クラスを介して)。

お役に立てれば


29
マークされたプロパティnewが仮想ベースのプロパティをオーバーライドしなくなったため、この回答は誤りです。特に、元の投稿のように、ベースプロパティがabstractの場合、newすべての抽象メンバーをオーバーライドする必要があるため、非抽象サブクラスでキーワードを使用することはできません。
ティムウィ

次のものは同じオブジェクトであるにもかかわらず、異なる結果を返すため、これは驚異的に危険Test t = new Test(); Base b = t; Console.WriteLine(t.BaseProperty)です。5をConsole.WriteLine(b.BaseProperty)与えると0を与え、後継者がこれをデバッグする必要がある場合は、はるかに遠くに移動した方がよいでしょう。
Mark Sowul 2014年

私はマークと同意します、これは非常に厄介です。両方のBaseProperty実装が同じ変数に基づいている場合、IMHOを使用しても問題ありません。IE:_basePropertyを保護し、_testBasePropertyを廃止します。また、実際にはオーバーライドしていないので、Baseの実装は仮想である必要はありません。
Steazy、

質問へのほとんどの回答は、有効な意見と入力を持つことができます。しかし、以前のコメントとは別に、この回答に対する私の主な批判は、実際の質問は、消費者が制御できないコードベースの抽象的なプロパティに関するものだと私は信じているということです。だから私の批判は、この答えは質問に合わないかもしれないということです。
Suamere 2015年

-1

基本クラスの読み取り専用プロパティは、このプロパティがクラス内から常に決定できる値を表すことを示します(たとえば、オブジェクトの(db-)コンテキストに一致する列挙値)。したがって、値を決定する責任はクラス内に留まります。

セッターを追加すると、ここで厄介な問題が発生します。値を、設定されている単一の可能な値以外に設定すると、検証エラーが発生します。

ただし、ルールには多くの場合例外があります。たとえば、1つの派生クラスでは、コンテキストが可能な列挙値を10中3に絞り込んでいる可能性は十分にありますが、このオブジェクトのユーザーは、どちらが正しいかを決定する必要があります。派生クラスは、値を決定する責任をこのオブジェクトのユーザーに委任する必要があります。 このオブジェクトのユーザーはこの例外十分に認識し、正しい値を設定する責任を負う必要があることを理解すること重要です。

このような状況での私の解決策は、プロパティを読み取り専用のままにして、例外をサポートするために派生クラスに新しい読み取り/書き込みプロパティを追加することです。元のプロパティのオーバーライドは、単に新しいプロパティの値を返します。新しいプロパティには、この例外のコンテキストを適切に示す適切な名前を付けることができます。

これは有効な発言もサポートしています:ギシュウによる「誤解をできるだけ起こさないようにする」。


1
ReadableMatrixサブタイプImmutableMatrixとを持つクラス(2つの引数を持つ読み取り専用のインデックス付きプロパティを持つ)によって、ぎこちないことは意味されませんMutableMatrix。マトリックスを読み取るだけで、将来的に変更されるかどうかを気にしないコードは、型のパラメーターを受け入れ、ReadableMatrix可変または不変のサブタイプで完全に満足することができます。
スーパーキャット2012年

追加のセッターは、クラス内のプライベート変数を変更できます。基本クラスからのいわゆる「読み取り専用」プロパティは、「クラス内から」(新しいプライベート変数を読み取ることによって)値を決定します。そこには問題がありません。また、たとえば、何List<T>.Countが割り当てられているかを確認してください。ただし、これは、クラスのユーザーが変更できないという点で「読み取り専用」であることを意味しません。リストアイテムを追加および削除することで、間接的にではありますが、非常に適切に変更できます。
またはMapper 2013

-2

ILレベルでは、読み取り/書き込みプロパティは2つのメソッド(ゲッターとセッター)に変換されるためです。

オーバーライドするときは、基礎となるインターフェースをサポートし続ける必要があります。セッターを追加できれば、クラスのインターフェースに関する限り、外界から見えないままになる新しいメソッドを効果的に追加することになります。

確かに、新しいメソッドを追加すること自体は互換性を損なうことはありませんが、非表示のままであるため、これを許可しないという決定は完全に理にかなっています。


2
ここでは「外の世界」はあまり気にしません。クラスに機能を追加したいと思います。これは、特定のクラスを認識し、ベースを認識しないコードにのみ常に表示されます。
ripper234 2008

@ ripper234マットボルトの答えを参照してください。新しいプロパティを派生クラスのユーザーにのみ表示する場合は、のnew代わりに使用する必要がありますoverride
Dan Berindei、

1
非表示のままではありません。新しいメソッドは「外部の世界から隠されたまま」になるため、新しいメソッドを子クラスに追加できないと言っているようなものです。もちろん、基本クラスの観点からは非表示のままですが、子クラスのクライアントからアクセスできます。(つまり、ゲッターが返す必要があるものに影響を与えるため)
Sheepy

シーピー、それは私が言っていることとはまったく違う。つまり、あなたが指摘したとおり、「基本クラスの観点から」それは隠されたままです。そのため、「クラスのインターフェースに関する限り」を追加しました。基本クラスをサブクラス化している場合、「外界」が基本クラスタイプのオブジェクトインスタンスを参照していることはほぼ確実です。彼らがあなたの具象クラスを直接使用する場合は、基本クラスとの互換性は必要ありませんnew
Ishmaeel、2011

-2

読み取り専用プロパティ(セッターなし)を持つクラスは、おそらくそれを正当な理由があるためです。たとえば、基礎となるデータストアがない場合があります。セッターを作成できるようにすると、クラスによって定められた契約が破られます。それは単に悪いOOPです。


賛成投票。これは非常に簡潔です。マイクロソフトは基本クラスのコンシューマが契約を破ることをなぜ許可する必要があるのですか 私の長くて混乱する答えのこの非常に短いバージョンに同意します。笑。基本クラスのコンシューマーである場合は、契約を破る必要はありませんが、いくつかの異なる方法で実装できます。
Suamere 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.