2017-07-25 7 views
0

私はいくつかの要素の "レベル"で作られたアプリケーションを持っています。いくつかの要素は0..Nの子要素の親であり、それ自体は0..Nの他の要素の親である。最上位タイプには親がなく、最下位レベルには子がありません。不思議な再帰的なテンプレートパターンのようなものと混同されています

つまり、A、B、Cの3つのタイプを考えてみましょう。Aのインスタンスは、Bの複数のインスタンスの親であり、それ自身が複数のインスタンスの親である。各インスタンスには(強く型付けされた)その親への参照。

私はAddChild、RemoveChild、GetChildIndexなどのような親クラスで同じいくつかのメソッドを持っています。私は親クラスごとにこれらのメソッドを複製しないために、すべての親クラスの基本クラスを持っていたいと思います。

親の基底クラスから派生する場合、親の型に応じて子の型自体を指定する必要があります。

これまでのところ、私はこの非常に複雑な設計が出ている:

public interface IChild<TParent> where TParent : ParentBase<IChild<TParent>> 
{ 
    TParent Parent { get; set; } 
} 

public class ParentBase<TChild> where TChild : IChild<ParentBase<TChild>> 
{ 
    public List<TChild> Children; 
} 

public class A : ParentBase<B> 
{ 
} 

public class B : ParentBase<C>, IChild<A> 
{ 
} 

public class C : IChild<B> 
{ 
} 

しかし、私はコンパイルエラーを取得しています:

Error CS0311 The type 'TemplateTest.IChild<TParent>' cannot be used as type parameter 'TChild' in the generic type or method 'ParentBase<TChild>'. There is no implicit reference conversion from 'TemplateTest.IChild<TParent>' to 'TemplateTest.IChild<TemplateTest.ParentBase<TemplateTest.IChild<TParent>>>'. Kbd2 C:\dev\Kbd2\TemplateTest.cs 6 Active 
Error CS0311 The type 'TemplateTest.ParentBase<TChild>' cannot be used as type parameter 'TParent' in the generic type or method 'IChild<TParent>'. There is no implicit reference conversion from 'TemplateTest.ParentBase<TChild>' to 'TemplateTest.ParentBase<TemplateTest.IChild<TemplateTest.ParentBase<TChild>>>'. Kbd2 C:\dev\Kbd2\TemplateTest.cs 11 Active 
Error CS0311 The type 'TemplateTest.B' cannot be used as type parameter 'TChild' in the generic type or method 'ParentBase<TChild>'. There is no implicit reference conversion from 'TemplateTest.B' to 'TemplateTest.IChild<TemplateTest.ParentBase<TemplateTest.B>>'. Kbd2 C:\dev\Kbd2\TemplateTest.cs 16 Active 
Error CS0311 The type 'TemplateTest.C' cannot be used as type parameter 'TChild' in the generic type or method 'ParentBase<TChild>'. There is no implicit reference conversion from 'TemplateTest.C' to 'TemplateTest.IChild<TemplateTest.ParentBase<TemplateTest.C>>'. Kbd2 C:\dev\Kbd2\TemplateTest.cs 20 Active 
Error CS1721 Class 'B' cannot have multiple base classes: 'ParentBase<C>' and 'IChild<A>' Kbd2 C:\dev\Kbd2\TemplateTest.cs 20 Active 
Error CS0311 The type 'TemplateTest.B' cannot be used as type parameter 'TParent' in the generic type or method 'IChild<TParent>'. There is no implicit reference conversion from 'TemplateTest.B' to 'TemplateTest.ParentBase<TemplateTest.IChild<TemplateTest.B>>'. Kbd2 C:\dev\Kbd2\TemplateTest.cs 24 Active 

は、私は、これはコンパイルできるかどうかさえわからないんだけどクラスはお互いに依存しているからです。私は何か間違っているのですか?ありがとうございました。

EDIT:未実装方法と、更新エラーリストが追加されました。

EDIT:子供はちょうど親クラスのように、代わりのインターフェースの基本クラスをインタフェースすることによって、例を簡素化。

EDIT:実際に一つだけの基本クラスがそのように許可されているが、私は子供ではなく、クラスのインターフェースに型バックなりました。

EDIT:私は2つの「どこで」制約のいずれかを削除すると、エラーがすべてなくなっています。彼らはお互いに依存しているからですか?

+0

テンプレートとC#の両方にタグを付けましたか? C#にはテンプレートがないジェネリックがあります。また、AのインスタンスがBの複数のインスタンスの親であるとします。どうしたらいいのですか? – ROX

+0

明らかに、定義は相互に再帰的であり(各反復でより複雑になるため)、これは機能しません。したがって、1つの制約を削除するか、再帰的でない制約で置き換える必要があります。 – Phil1970

+0

@ Phil1970ご協力いただきありがとうございます。結局私はこのデザインを落として、何か他のものに行きます。その半分しか存在しない場合は、ポイントはありません。私は好奇心だけど。コンパイラが、これらのエラーの代わりに循環依存があると明示的に伝えていないのはなぜですか?それは彼らが単純にこのエラーのケースをカバーしなかったからですか? – Virus721

答えて

0

現在のアプローチの問題は、何らかの種類の奇妙な再帰に依存していることです。

  • を、子が親
  • を持つ人であり、その親が親
  • を持っている子供を持つ人で、その子供が子供を持つ親を持つ人である:それは言ってようなものです親がある
  • ...これは動作しません!

あなたはそれについてもう一度考えた場合、親/子関係は二つのタイプが含まれます。

public interface IChild<P, C> where P : IParent<P, C> where C : IChild<P, C> { 
    P Parent { get; set; } 
} 

public interface IParent<P, C> where P : IParent<P, C> where C : IChild<P, C> { 
    IList<C> Children { get; } 
} 

これが動作してもよいがIParentに型paremeter Pが使用されていないので、それは、非常に複雑に見えますし、 IChildにタイプパラメータCは使用されません。

しかし、私たちは正しい方向に進んでいると思います。あなたがインターフェイスを簡素化し、ParentBaseに汎用的な制約を追加する場合は、あなたがより理解しやすい何かを得る:この設計では

public interface IChild<P> { 
    P Parent { get; set; } 
} 

public interface IParent<C> { 
    IList<C> Children { get; } 
} 

public class ParentBase<P, C> : IParent<C> where C : IChild<P> { 
    public IList<C> Children { get; } = new List<C>(); 
} 

を、あなたはseparated concernsを持っている:

  • 両親
  • のためのインタフェースのためのインタフェースを子
  • と基本クラスの実装

あなたの例では動作します:

public class A : ParentBase<A, B> { 
} 

public class B : ParentBase<B, C>, IChild<A> { 
    public A Parent { get; set; } 
} 

public class C : IChild<B> { 
    public B Parent { get; set; } 
} 

public class Demo { 
    public static void Test() { 
     var a = new A(); 
     var b = new B() { Parent = a }; 
     var c = new C() { Parent = b }; 
     a.Children.Add(b); 
     b.Children.Add(c); 
     System.Console.WriteLine(c.Parent.Parent == a); 
    } 
} 

注意すべきマイナーその他の詳細情報があります。公表リストの

  • は、その実装から抽象へのインタフェースIList<T>を使用するのが一般的
  • 公共分野(のようなchildrenParentBase)は珍しく、ほとんどの人が読み取り専用プロパティを使用してリスト自体をカプセル化します。
関連する問題