2017-11-14 25 views
-2

この基本クラスから派生したクラスの型に一致するメンバーを宣言する抽象基本クラスがあるとします。派生クラスの型である抽象基本クラスのメンバーを宣言する方法はありますか?

public abstract class BaseClass 
{ 
    protected BaseClass parent; 
} 

public class DerivedClass1 : BaseClass 
{ 
    // parent could be of type DerivedClass2 
} 

public class DerivedClass2 : BaseClass 
{ 
    // parent could be of type DerivedClass1 
} 

各派生クラスでparentフィールドがBaseClassから派生何もすることができますので、これは動作しません。私はDerivedClass1parentフィールドがDerivedClass1であることを保証したいと思います。だから多分ジェネリックを使うべきだと思っています。

public abstract class BaseClass<T> where T : BaseClass<T> 
{ 
    protected T parent; 
} 

これは混乱しているように見えるかもしれませんが、コンパイルされます。基本的にはparentがタイプTであり、汎用BaseClassから派生しなければならないと言っています。だから今派生クラスは、次のようになります。

public class DerivedClass : BaseClass<DerivedClass> 
{ 
    // parent is of type DerivedClass 
} 

を問題は、私は私がDerivedClassを宣言する際に、型マッチングを自分自身を強制しなければならないということです。このような何かをやってから誰かを停止することは何もありません:ベースで宣言されたメンバの型が派生型が一致することを確認されるように

public class DerivedClass1 : BaseClass<DerivedClass2> 
{ 
    // parent is of type DerivedClass2 
} 

は、C#が、これを行う方法がありますか?

私は、これが何をこのC++質問をしようとしていたに似ていると思う:Abstract base class for derived classes with functions that have a return type of the derived class

+2

これはCRTPと呼ばれます。 – SLaks

+0

親プロパティを特定の型にしたい場合、その型が継承している基本クラスを心配する必要があるのはなぜですか?また、抽象クラスでこのプロパティを宣言する必要はありません。適切なクラスでプロパティを作成するだけです。 –

+0

あなたの質問は不明です。つまり、あなたが望むものは明らかです。しかし、あなたは今、C#がそのような制約を提供していないことを知っていたはずです。さらに重要な点は、この制約が有用であると考えるあなたの質問は、_why_に関する詳細をゼロにしていることです。型パラメータ 'T'が実際に宣言型と同じでない場合、なぜあなたにとって重要なのでしょうか?それは基本クラスまたは派生クラスの実装にどのような影響を与えますか?あなたは何ができないでしょうか、あなたはそのような拘束をすることができますか?あなたの期待についての詳細を記入してください。 –

答えて

1

私が正しくあなたの要件を理解していれば、あなたは継承関係を持つクラスのシリーズを持って、そしてあなたがそれらを整理したいです各インスタンスが同じタイプの親を持ち、同じタイプのもののみを持つツリー構造です。それは興味深い問題です。

ビットのためにこれをヌードリングした後、私はあなたが

  1. 継承関係
  2. セットとクラスのセットを持っているように、あなたは、2つの平行であるが、関連するオブジェクトグラフに要件を分離示唆するかもしれません最初のセットからのクラスのいずれかを含むことができ、型厳密な親と子を持つクラスのリストです。

最初に、互いに継承するクラスの最初のセットを宣言しましょう。今のところはNodeビットを無視してください。

public class BaseClass 
{ 
    public Node ContainerNode { get; set; } 
} 

public class DerivedClass1 : BaseClass 
{ 
} 

public class DerivedClass2 : BaseClass 
{ 
} 

これらのクラスはあまり効果がありませんが、単なる例です。

次に、ツリーに参加できる別のクラスセットを設定しましょう。ツリー内の各要素はNodeと呼ばれます。

//Basic node 
public class Node 
{ 
} 

//A node that can contain a T (which must be a BaseClass or derived from one) 
public class Node<T> : Node where T : BaseClass 
{ 
    public T Parent { get; set; } 
    public T This { get; set; } 

    public Node(T innerClass) 
    { 
     this.This = innerClass; 
     innerClass.ContainerNode = this; 
    } 
} 

ここでは、タイプセーフティを実行するために必要なものがすべて用意されています。私たちは、このようなあなたの継承階層内のクラスを作成することができます。

var child1 = new Node<DerivedClass1>(new DerivedClass1()); 
    var parent1 = new Node<DerivedClass1>(new DerivedClass1()); 
    child1.Parent = parent1.This; 

は、我々が誤ってDerivedClass1とDerivedClass2をミックスするとどうなるか見てみましょう:あなたが見ることができる

var child2 = new Node<DerivedClass2>(new DerivedClass2()); 
    var parent2 = new Node<DerivedClass1>(new DerivedClass1()); //Oops 
    child2.Parent = parent2.This; //Does not compile 

ように、Parentプロパティはタイプセーフです。

このようなすべてのもの^^^^はちょっと面倒なので、いくつかのヘルパーメソッドを追加してクリーンアップしましょう。今

public class Node 
{ 
    public T GetParent<T>() where T : BaseClass 
    { 
     return ((Node<T>)this).Parent; 
    } 

    static public Node<T> Create<T>(T innerClass) where T : BaseClass 
    { 
     return new Node<T>(innerClass); 
    } 

    static public T GetParent<T>(T child) where T: BaseClass 
    { 
     return child.ContainerNode.GetParent<T>(); 
    } 

    static public implicit operator T (Node<T> input) 
    { 
     return input.This; 
    } 
} 

、コンパイラは<T>引数を推測することができるので、私たちの宣言は非常に滑らかな印象です。

var child1 = Node.Create(new DerivedClass1()); 
    var parent1 = Node.Create(new DerivedClass1()); 
    child1.Parent = parent1; 

と派生クラスのいずれかが、自身の親を探して、それは簡単です:

public class DerivedClass1 : BaseClass 
{ 
    protected DerivedClass1 Parent 
    { 
     get 
     { 
      return Node.GetParent(this); //This is type safe! 
     } 
    } 
} 

これに反対するひとつは、コーダーがこのNodeレイヤーを処理しないようにすることです。我々はimplicitキャストをup設定because Well、theyは、しないでください。

Node<DerivedClass1> a = Node.Create(new DerivedClass1()); 
    DerivedClass1 b = a; //Works!!! And is type-safe. 

Full working code on DotNetFiddle

+0

非常に印象的! 'ContainerNode'や' Parent'や 'This'のようなメンバーは公開されていると心配していますが、おそらく内部的なものかもしれません。 –

+1

合意。あるいは、それらを 'public readonly'にして、コンストラクタの引数で設定することもできます。もしそれらが不変であれば、それは大きな懸念ではない。 –

関連する問題