151

次の 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' にはオーバーライド可能な set アクセサーがないため、オーバーライドできません

4

16 に答える 16

43

主な理由は単純に、これが他の方法で機能するには構文が明示的すぎるためだと思います。このコード:

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

getと の両方setがオーバーライドであることは明らかです。基本クラスには何もないsetため、コンパイラは文句を言います。基本クラスで定義されていないメソッドをオーバーライドできないのと同様に、セッターもオーバーライドできません。

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

次の構文はうまくいくと思いますが、Eric Lippert が言い続けているように、このような小さな機能を実装することは、依然として多大な労力を要します...

public int MyProperty
{
    override get { ... } // not valid C#
    set { ... }
}

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

public int MyProperty { override get; set; } // not valid C#
于 2010-04-03T12:06:27.693 に答える
19

今日、まったく同じ問題に出くわしました。これが必要な非常に正当な理由があると思います。

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

以下は、今日私が必要としていたものの簡単な例です。これを回避するためだけに、インターフェイスにセッターを追加する必要がありました。セッターを追加し、たとえば SetProp メソッドを追加しない理由は、インターフェイスの特定の実装が、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 セント」投稿であることはわかっていますが、元の投稿者に感じており、これが良いことであると合理化しようとするのは奇妙に思えます。インターフェース。

また、override の代わりに new を使用することについての言及はここでは当てはまりません。単純に機能せず、機能したとしても、必要な結果、つまりインターフェイスで記述された仮想ゲッターが得られません。

于 2010-09-09T16:18:37.873 に答える
11

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

同じ誤解が助長されたためか、おそらく文法を単純化したために、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;
   }
于 2012-09-13T20:50:42.577 に答える
4

新しいプロパティを作成することで、問題を回避できる可能性があります。

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

int IBase.Bar { 
  get { return Bar; }
}
于 2008-09-17T12:37:06.807 に答える
1

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

次のようなことはできません。

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

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

于 2008-09-20T11:03:32.240 に答える
1

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

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

于 2011-12-19T02:49:49.500 に答える
0

質問のタイトルを変更して、質問が抽象プロパティのオーバーライドのみに関するものであること、または質問がクラスの取得専用プロパティの一般的なオーバーライドに関するものであることを詳しく説明する必要があります。


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

そのコードは役に立たない。基本クラスだけでは、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; }
}

後者の場合(クラスの取得専用プロパティをオーバーライドする)

すべての非抽象プロパティにはセッターがあります。それ以外の場合は役に立たないので、使用する必要はありません。Microsoft は、ユーザーがやりたいことを行うことを許可する必要はありません。理由: セッターは何らかの形で存在し、Veerryyが望むことを簡単に実現できます。

基本クラス、または でプロパティを読み取ることができる任意のクラスには、そのプロパティの公開されたセッターが{get;}いくつかあります。メタデータは次のようになります。

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 が何かを変更する必要はありません。実装クラスのセットアップ方法を選択し、コンストラクターを使用せずに、基本クラスをすべて使用するか、まったく使用しないかを選択できます。

于 2015-10-22T20:13:49.750 に答える
-1

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

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

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

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

これはまた、ギス人による「誤解が生じにくいようにする」という正当な発言を支持している。

于 2012-07-13T12:42:15.073 に答える
-1

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

于 2008-09-17T12:17:39.617 に答える
-1

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

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

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

お役に立てれば

于 2010-08-21T20:06:44.347 に答える
-2

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

于 2008-09-17T13:15:24.703 に答える
-3

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

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

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

于 2008-09-17T12:17:11.783 に答える
-7

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

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

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 という名前の読み取りプロパティを持つことを期待する」という契約です。個人的には、インターフェイスを見た場合、読み取り専用であると仮定する可能性ははるかに低くなります。インターフェイスに取得専用プロパティが表示されると、「どの実装でもこの属性バーが公開される」と読みます...基本クラスでは、「バーは読み取り専用プロパティです」とクリックします。もちろん、技術的には、契約を破っているのではありません..もっとやっています。ある意味ではその通りですね..最後に「できるだけ誤解が生じないように」と言って締めくくりたいと思います。

于 2008-09-17T12:17:54.047 に答える