Search code examples
c#genericsinterfacenested-generics

List entries conform to both class and interface?


I have a the following C# classes and interfaces:

class NativeTool
class NativeWidget: NativeTool
class NativeGadget: NativeTool
// above classes defined by the API I am using.  Below classes and interfaces defined by me.
interface ITool
interface IWidget: ITool
interface IGadget: ITool
class MyTool: NativeTool, ITool
class MyWidget: NativeWidget, IWidget
class MyGadget: NativeGadget, IGadget

Now, I would like MyTool to keep a list of children. The children will all conform to ITool and inherit from NativeTool. The classes MyTool, MyWidget, and MyGadget all fit these criteria.

My question is, is there any way to tell MyTool that its children will always inherit from both NativeTool and ITool? I can do one or the other easily enough. But both?


Solution

  • This appears to do it. An annoying number of wrappers, but it gets the job done, without duplicating storage.

    public interface ITool { }
    public interface IWidget : ITool { }
    public class NativeTool { }
    public class NativeWidget : NativeTool { }
    public class MyTool : NativeTool, ITool, INativeTool {
      public MyTool() {
        this.Children = new List<INativeTool>();
      }
      public ITool InterfacePayload { get { return this; } }
      public NativeTool NativePayload { get { return this; } }
      public List<INativeTool> Children { get; set; }
      public NativeTool NativeChild(int index) {
        return this.Children[index].NativePayload;
      }
      public ITool InterfaceChild(int index) {
        return this.Children[index].InterfacePayload;
      }
      public void AddChild(MyTool child) {
        this.Children.Add(child);
      }
      public void AddChild(MyWidget child) {
        this.Children.Add(child);
      }
    }
    public class MyWidget : NativeWidget, IWidget, INativeTool {
      public ITool InterfacePayload { get { return this; } }
      public NativeTool NativePayload { get { return this; } }
    }
    public interface INativeTool {
      // the two payloads are expected to be the same object.  However, the interface cannot enforce this.
      NativeTool NativePayload { get; }
      ITool InterfacePayload { get; }
    }
    public class ToolChild<TPayload>: INativeTool where TPayload : NativeTool, ITool, INativeTool {
      public TPayload Payload { get; set; }
      public NativeTool NativePayload {
        get {return this.Payload;}
      }
      public ITool InterfacePayload {
        get { return this.Payload; }
      }
    }