トップ回答者
互いに所有する2個のクラスをジェネリックで定義したい

質問
-
C#のジェネリックについてお教えください。
2個のジェネリックのクラスがあり、互いのインスタンスをプロパティで保持させたいです。
ただし、互いの型を型パラメータで指定させたいです。
具体的には以下のようなのですが、コンパイルが通りません。
何かうまい実装方法がありましたら、お教えください。
■例
Parentクラス、Childクラス を次のようにジェネリックで定義したいです。
・ParentはChildの型を型パラメータで指定する。
・ChildはParentの型を型パラメータで指定する。
ここで、型パラメータに条件を設定して、互いのクラス、および派生クラス以外の
インスタンスは保持させないようにしたいです。
public class Parent<TChild>
where TChild : Child<Parent<TChild>>
{
public TChild Child {get; set;}
}
public class Child<TParent>
where TParent : Parent<Child<TParent>>
{
public TParent parent {get; set;}
}
■コンパイルエラー
エラー 1 型 'ConsoleApplication1.Impl.Parent<TChild>' はジェネリック型またはメソッド 'ConsoleApplication1.Impl.Child<TParent>' 内で型パラメーター 'TParent' として使用できません。'ConsoleApplication1.Impl.Parent<TChild>' から 'ConsoleApplication1.Impl.Parent<ConsoleApplication1.Impl.Child<ConsoleApplication1.Impl.Parent<TChild>>>' への暗黙的な参照変換がありません。 E:\kazuhiro\Software\test\WPF\Behavior\ConsoleApplication1\ConsoleApplication1\Impl\Parent.cs 9 18 ConsoleApplication1
エラー 2 型 'ConsoleApplication1.Impl.Child<TParent>' はジェネリック型またはメソッド 'ConsoleApplication1.Impl.Parent<TChild>' 内で型パラメーター 'TChild' として使用できません。'ConsoleApplication1.Impl.Child<TParent>' から 'ConsoleApplication1.Impl.Child<ConsoleApplication1.Impl.Parent<ConsoleApplication1.Impl.Child<TParent>>>' への暗黙的な参照変換がありません。 E:\kazuhiro\Software\test\WPF\Behavior\ConsoleApplication1\ConsoleApplication1\Impl\Child.cs 9 18 ConsoleApplication1
回答
-
難しいですね。
無駄に複雑になっている感がありますが、以下のような感じでどうでしょうか。static void Main(string[] args) { var a1 = new AParent(); var a2 = new AChild(); a1.Child = a2; a2.Parent = a1; var b1 = new BParent(); var b2 = new BChild(); b1.Child = b2; b2.Parent = b1; } interface IParent { } interface IChild { } abstract class ParentBase<TChild> : IParent where TChild : IChild { public TChild Child { get; set; } } abstract class ChildBase<TParent> : IChild where TParent : IParent { public TParent Parent { get; set; } } class AParent: ParentBase<AChild> { } class AChild : ChildBase<AParent> { } class BParent : ParentBase<BChild> { } class BChild : ChildBase<BParent> { }
多分、ジェネリック以外でもっと良いアプローチがあるような気がします。
オブジェクトのモデル例まで出ると代替案が出るかもしれませんね。- 編集済み Tak1waMVP, Moderator 2015年2月25日 1:10
- 回答の候補に設定 星 睦美 2015年2月27日 1:00
- 回答としてマーク 星 睦美 2015年3月2日 4:57
-
この問題(パラレル継承階層)はある程度妥協しないと仕方がないと思います。
私は共通の基底クラスを設けた上でお互いを示すプロパティを上書きしています。
NetFrameworkの DataTable→TypedDataTable, DataRow→TypedDataRow も同様です。
public class Parent { public TChild Child { get; set; } } public class Child { public TParent Parent {get; set; } } public class Parent<TChild> : Parent where TChild : Child { public new TChild Child { get { return (TChild)base.Child; } set { base.Child = value; } } } public class Child<TParent> : Child where TParent : Parent { public new TParent Parent get { return (TParent)base.Parent; } set { base.Parent = value; } } }
どうしてもダウンキャスト無しと言うことであれば最後のリンクが参考になるかもわかりません。
参考
http://systemartlaboratory.com/parallelinheritance.html 拙作
http://csharper.blog57.fc2.com/blog-entry-130.html
http://csharper.blog57.fc2.com/blog-entry-133.html (1階層なら書けるが複雑)
http://systemartlaboratory.com/
すべての返信
-
こんにちは。
定義のみであればインターフェイスを使って実現してみては如何ですか。
interface IHoge { } class Parent<TChild> : IHoge where TChild : IHoge { public TChild Child { get; set; } } class Child<TParent> : IHoge where TParent : IHoge { public TParent parent { get; set; } }
ただ、これ無理じゃないですかね…。
宣言時にはジェネリック型は確定していないといけないと思うので、Parent<Child<Parent<Child<Parent<...>>>>> hoge;
このように循環するので宣言できないのでは。
どういう目的のクラスなんでしょうか?
代替案を考えたほうが良い気がします。- 編集済み Tak1waMVP, Moderator 2015年2月24日 14:18
-
ありがとうござます。
残念ながら、インターフェイスでも、型パラメータに質問に記した制約を設定すると同じエラーが発生しました。
指定した型、およびその派生クラスのみを設定できるようにしたいため、この条件は外したくありません。
> どういう目的のクラスなんでしょうか?
> 代替案を考えたほうが良い気がします。
クラス間に親子の関連を持たせたいです。
決まったクラスと関連を持たせたいので、制約は外したくないです。
そして、このようなクラスが複数あるので、ジェネリックで実現できないかと思いました。
- 編集済み kyasui 2015年2月24日 23:31
-
難しいですね。
無駄に複雑になっている感がありますが、以下のような感じでどうでしょうか。static void Main(string[] args) { var a1 = new AParent(); var a2 = new AChild(); a1.Child = a2; a2.Parent = a1; var b1 = new BParent(); var b2 = new BChild(); b1.Child = b2; b2.Parent = b1; } interface IParent { } interface IChild { } abstract class ParentBase<TChild> : IParent where TChild : IChild { public TChild Child { get; set; } } abstract class ChildBase<TParent> : IChild where TParent : IParent { public TParent Parent { get; set; } } class AParent: ParentBase<AChild> { } class AChild : ChildBase<AParent> { } class BParent : ParentBase<BChild> { } class BChild : ChildBase<BParent> { }
多分、ジェネリック以外でもっと良いアプローチがあるような気がします。
オブジェクトのモデル例まで出ると代替案が出るかもしれませんね。- 編集済み Tak1waMVP, Moderator 2015年2月25日 1:10
- 回答の候補に設定 星 睦美 2015年2月27日 1:00
- 回答としてマーク 星 睦美 2015年3月2日 4:57
-
この問題(パラレル継承階層)はある程度妥協しないと仕方がないと思います。
私は共通の基底クラスを設けた上でお互いを示すプロパティを上書きしています。
NetFrameworkの DataTable→TypedDataTable, DataRow→TypedDataRow も同様です。
public class Parent { public TChild Child { get; set; } } public class Child { public TParent Parent {get; set; } } public class Parent<TChild> : Parent where TChild : Child { public new TChild Child { get { return (TChild)base.Child; } set { base.Child = value; } } } public class Child<TParent> : Child where TParent : Parent { public new TParent Parent get { return (TParent)base.Parent; } set { base.Parent = value; } } }
どうしてもダウンキャスト無しと言うことであれば最後のリンクが参考になるかもわかりません。
参考
http://systemartlaboratory.com/parallelinheritance.html 拙作
http://csharper.blog57.fc2.com/blog-entry-130.html
http://csharper.blog57.fc2.com/blog-entry-133.html (1階層なら書けるが複雑)
http://systemartlaboratory.com/
-
質問及び返信を読んでも何を求められているのかわかりません。
「決まった相手」でも「親子関係」でも「所有関係」でも表現は何だっていいんですが、同じことを指しているようにしか読めませんし、それが何を求めているのか、もしくは逆に私の提示した例で何が不足しているのか、読み取れません。
質問内容において、ジェネリック型はいくつ登場するのでしょうか?
- Parent<T>とChild<T>の2つ
- Parent<T1>とChild<T1>、Parent<T2>とChild<T2>の型としてはやっぱり2つ
- Parent1<T1>とChild1<T1>、Parent2<T2>とChild2<T2>の4つ
1や2であれば(決まった相手|親子関係|所有関係)が1つに定められ、型パラメーター等で制約を付ける必要はそもそもないはずですが?