189

多くのコンストラクターと仮想メソッドを持つ基本クラスを想像してください

public class Foo
{
   ...
   public Foo() {...}
   public Foo(int i) {...}
   ...
   public virtual void SomethingElse() {...}
   ...
}

そして今、仮想メソッドをオーバーライドする子孫クラスを作成したいと思います:

public class Bar : Foo 
{
   public override void SomethingElse() {...}
}

そして、さらにいくつかのことを行う別の子孫:

public class Bah : Bar
{
   public void DoMoreStuff() {...}
}

すべてのコンストラクターを Foo から Bar と Bah にコピーする必要がありますか? そして、Foo でコンストラクターの署名を変更した場合、Bar と Bah でそれを更新する必要がありますか?

コンストラクタを継承する方法はありませんか? コードの再利用を促進する方法はありませんか?

4

14 に答える 14

127

はい、派生ごとに意味のあるコンストラクターを実装し、baseキーワードを使用してそのコンストラクターを適切な基本クラスthisに誘導するか、キーワードを使用してコンストラクターを同じクラス内の別のコンストラクターに誘導する必要があります。

コンパイラーがコンストラクターの継承について仮定した場合、オブジェクトがどのようにインスタンス化されたかを適切に判断できなくなります。ほとんどの場合、なぜこれほど多くのコンストラクターが存在するのかを検討し、それらを基本クラスで 1 つまたは 2 つに減らすことを検討する必要があります。派生クラスは、次のような定数値を使用してそれらの一部をマスクしnull、コンストラクターを介して必要なもののみを公開できます。

アップデート

C#4 では、構成ごとに 1 つのコンストラクターを使用するのではなく、既定のパラメーター値を指定し、名前付きパラメーターを使用して、1 つのコンストラクターで複数の引数構成をサポートすることができました。

于 2008-10-21T19:01:56.723 に答える
69

387人のコンストラクター?? それがあなたの主な問題です。代わりにこれはどうですか?

public Foo(params int[] list) {...}
于 2008-10-21T19:03:30.293 に答える
42

はい、387 個のコンストラクターをすべてコピーする必要があります。それらをリダイレクトすることで、いくつかの再利用を行うことができます:

  public Bar(int i): base(i) {}
  public Bar(int i, int j) : base(i, j) {}

しかし、それはあなたができる最善のことです。

于 2008-10-21T19:03:09.590 に答える
27

コンストラクターを同じレベルの継承で他のコンストラクターにリダイレクトすることもできることを忘れないでください。

public Bar(int i, int j) : this(i) { ... }
                            ^^^^^
于 2008-10-21T20:49:53.587 に答える
10

もう 1 つの簡単な解決策は、パラメーターをプロパティとして含む構造体または単純なデータ クラスを使用することです。そうすれば、すべてのデフォルト値と動作を事前に設定し、「パラメーター クラス」を単一のコンストラクター パラメーターとして渡すことができます。

public class FooParams
{
    public int Size...
    protected myCustomStruct _ReasonForLife ...
}
public class Foo
{
    private FooParams _myParams;
    public Foo(FooParams myParams)
    {
          _myParams = myParams;
    }
}

これにより、(場合によっては) 複数のコンストラクターの混乱が回避され、強力な型指定、既定値、およびパラメーター配列によって提供されないその他の利点が得られます。また、Foo から継承するものはすべて、必要に応じて FooParams にアクセスしたり、追加したりできるため、継承が容易になります。それでもコンストラクターをコピーする必要がありますが、常に (ほとんどの場合) (原則として) (少なくとも今のところ) 必要なコンストラクターは 1 つだけです。

public class Bar : Foo
{
    public Bar(FooParams myParams) : base(myParams) {}
}

オーバーロードされた Initailize() と Class Factory Pattern のアプローチの方が優れているのが本当に気に入っていますが、スマート コンストラクターが必要な場合もあります。ちょっとした考え。

于 2011-11-15T22:14:58.637 に答える
7

クラスのように、オーバーロードされた仮想メソッドFooを作成できませんか? Initialise()その後、それらはサブクラスで利用可能になり、さらに拡張可能になりますか?

public class Foo
{
   ...
   public Foo() {...}

   public virtual void Initialise(int i) {...}
   public virtual void Initialise(int i, int i) {...}
   public virtual void Initialise(int i, int i, int i) {...}
   ... 
   public virtual void Initialise(int i, int i, ..., int i) {...}

   ...

   public virtual void SomethingElse() {...}
   ...
}

多くのデフォルトのプロパティ値があり、頻繁にヒットしない限り、これによりパフォーマンス コストが高くなることはありません。

于 2008-10-21T21:03:46.680 に答える
6
public class BaseClass
{
    public BaseClass(params int[] parameters)
    {

    }   
}

public class ChildClass : BaseClass
{
    public ChildClass(params int[] parameters)
        : base(parameters)
    {

    }
}
于 2010-09-30T14:20:40.047 に答える
5

個人的には、これはマイクロソフト側の間違いだと思います。プログラマーが基本クラスのコンストラクター、メソッド、およびプロパティの可視性をオーバーライドできるようにし、コンストラクターが常に継承されるようにする必要がありました。

このようにして、必要なすべてのコンストラクターを追加するのではなく、不要なコンストラクターを単純にオーバーライドします (可視性を低くします。つまり、プライベート)。Delphi はこのようにしていますが、私はそれが恋しいです。

たとえば、System.IO.StreamWriter クラスをオーバーライドする場合は、7 つのコンストラクターすべてを新しいクラスに追加する必要があります。また、コメントを付けるのが好きな場合は、それぞれにヘッダー XML を付けてコメントする必要があります。さらに悪いことに、メタデータ ビューは XML コメントを適切な XML コメントとして配置しないため、1 行ずつコピーして貼り付ける必要があります。マイクロソフトはここで何を考えていたのでしょうか?

私は実際に、メタデータ コードを貼り付けることができる小さなユーティリティを作成しました。このユーティリティは、オーバーライドされた可視性を使用して XML コメントに変換します。

于 2011-07-21T23:49:42.757 に答える
3

問題は、Bar と Bah が 387 個のコンストラクターをコピーしなければならないことではなく、Foo が 387 個のコンストラクターを持っていることです。Foo は明らかに多くのことを行います - リファクタリングは迅速に行います! また、コンストラクターに値を設定する本当に正当な理由がない限り (パラメーターなしのコンストラクターを提供する場合は、おそらくそうしません)、プロパティの取得/設定を使用することをお勧めします。

于 2008-10-21T19:08:19.877 に答える
2

いいえ、387 個のコンストラクターすべてを Bar と Bah にコピーする必要はありません。Bar と Bah は、Foo で定義するコンストラクターの数に関係なく、好きなだけコンストラクターを持つことができます。たとえば、Foo の 212 番目のコンストラクターで Foo を構築する Bar コンストラクターを 1 つだけ持つことを選択できます。

はい、Bar または Bah が依存する Foo で変更するコンストラクターは、それに応じて Bar および Bah を変更する必要があります。

いいえ、.NET でコンストラクターを継承する方法はありません。ただし、サブクラスのコンストラクター内で基本クラスのコンストラクターを呼び出すか、定義した仮想メソッド (Initialize() など) を呼び出すことで、コードを再利用できます。

于 2008-10-21T19:15:58.550 に答える
1

C++ 仮想コンストラクターのイディオムのバージョンを適応させることができる場合があります。私の知る限り、C# は共変の戻り値の型をサポートしていません。それは多くの人々のウィッシュリストに載っていると思います。

于 2008-10-21T19:17:04.163 に答える
0

コンストラクターが多すぎるということは、設計が壊れていることを示しています。コンストラクターが少なく、プロパティを設定できるクラスの方が優れています。プロパティを本当に制御する必要がある場合は、同じ名前空間内のファクトリを検討し、プロパティ セッターを内部にします。クラスをインスタンス化し、そのプロパティを設定する方法をファクトリに決定させます。ファクトリには、オブジェクトを適切に構成するために必要な数のパラメータを取るメソッドを含めることができます。

于 2008-10-21T19:05:58.413 に答える