2012-04-13 13 views
7

子のオブジェクトを画面にレンダリングするライブラリを作成しています。子オブジェクトは抽象クラスであり、このライブラリのユーザーがこの抽象クラスから独自の子を派生させることを意図しています。汎用インターフェイスへのC#型キャスト

public abstract class Child : IRenderable {} 

public interface IParent<T> where T : Child 
{ 
    IEnumerable<T> Children { get; } 
} 

合併症ではなく、私がIRenderablesの束を持って、私はで動作するようにIParentのリストを持っていないということです。図書館の利用者は、このような何かを書くことが予想されます。

引き分けに
public class Car : IRenderable { } 
public class Cow : IRenderable, IParent<Calf> { } 
public class Calf : Child { } 

// note this is just an example to get the idea 
public static class App 
{ 
    public static void main() 
    { 
     MyLibraryNameSpace.App app = new MyLibraryNameSpace.App(); 
     app.AddRenderable(new Car()); // app holds a list of IRenderables 
     app.AddRenderable(new Cow()); 
     app.Draw(); // app draws the IRenderables 
    } 
} 

()、ライブラリべきキャストとはIRenderableもIParentであるかどうかを確認してください。しかし、私は子牛について知らないので、私は牛を投げるべきか分からない。

// In Draw() 
foreach(var renderable in Renderables) 
{ 
    if((parent = renderable as IParent<???>) != null) // what to do? 
    { 
     foreach(var child in parent.Children) 
     { 
      // do something to child here. 
     } 
    } 
} 

どうすればこの問題を解決できますか?これは共分散ジェネリックスと何か、それとも何か(私は共分散概念に精通していません)ですか?

答えて

9

のみリターンタイプT項目をIParent<T>ので、あなたはそれがout modifier使用して共変で作ることができますその共分散は、ヨuは(単に話す)のオブジェクトを返すだけです。例えば、できるだけ早くあなたが、そうでない場合は、次のタイプセーフでないコードを書くことができているので、あなたのIParentインターフェース、共分散を破る必要があります(=コンパイラが文句を言うだろう)にAddChild(T)メソッドを追加する:

IParent<Child> parent = renderable as IParent<Child>; 
parent.AddChild(new Kitten()); // can't work if parent is really a Cow. 
+0

'IParent 'は正当ではない( 'T:Child'条件を満たすことはできません)、実際には*スーパークラスにはなりません*むしろ、コンパイラとランタイムが分散を利用することができます。 –

+0

@MarcGravell:ありがとう、修正されました。制約を見過ごした。 – Heinzi

+1

編集内容に余分な説明があります。本当に助けになりました。ありがとう。 – Jake

1

あなたは中間の非汎用インタフェースIParent実現することができます。

public interface IParent 
{ 
    IEnumerable<Child> Children { get; } 
} 

public interface IParent<T> : IParent 
    where T: Child 
{ 
    IEnumerable<T> Children { get; } 
} 

をそしてあなたの関数でIParentにキャスト。

IParent<Child> parent = renderable as IParent<Child>; // works for Cow 

注:

public interface IParent<out T> where T : Child 
{ 
    IEnumerable<T> Children { get; } 
} 

これはIParent<anything>コンバーチブルIParent<Child>になるだろう:

+0

C#4.0以前はこれが限界でしたが、C#4.0以降では、Heinziの答えに概説されている分散アプローチが一般的に望ましいです。 –

+0

これは3.0で動作することを知っておくと便利です。 – Jake

1

何かを一緒に次の行?

static void draw(List<IRenderable> renderables) 
{ 
    foreach (IRenderable render in renderables) 
    { 
     if (render is IParent<Child>) 
     { 
      foreach (Child c in ((IParent<Child>)render).Children) 
      { 
       //do something with C? 
      } 
     } 
    } 
} 
関連する問題