C # type casting to a common interface

I am writing a library that displays a bunch of child objects on the screen. The child object is abstract, and it is intended for users of this library to get their own child from this abstract class.

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

The complication is that I do not have an IParent list to work with; instead, I have an IRenderables group. The library user is expected to write something like this:

 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 } } 

In Draw (), the library needs to drop and check if the IRenderable is also IParent. However, since I do not know about the calf, I do not know what to abandon the cow.

 // 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. } } } 

How can I solve this problem? Is this something in common with covariant generics or something else (I'm not familiar with the concept of covariance)?

+7
source share
3 answers

Since IParent<T> only returns elements of type T , you can make it covariant using the out modifier:

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

This would make IParent<anything> convertible to IParent<Child> :

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

Note that covariance only works as long as you return only objects of type T (just saying). For example, as soon as you add the AddChild(T) method to your IParent interface, the covariance should break (= the compiler will complain), because otherwise the following unsafe code like:

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

You can implement an intermediate non-generic IParent interface:

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

And then let's move on to IParent in your function.

+1
source

Something in the following lines?

 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? } } } } 
+1
source

All Articles