Thursday, February 17, 2011

Generic Parent For Generic Class

I am using a fictional example for this. Say, I have a Widget class like:

abstract class Widget
{
Widget parent;
}

Now, my other classes would be derived from this Widget class, but suppose I want to put some constraint in the class while defining the derived types such that only a particular "type" of widget can be parent to a particular type of Widget.

For example, I have derived two more widgets from the Widget class, WidgetParent and WidgetChild. While defining the child class, I want to define the type of parent as WidgetParent, so that I dont have to type cast the parent every time I use it.

Precisely, what I would have liked to do is this:

// This does not works!
class Widget<PType>: where PType: Widget
{
    PType parent;
}

class WidgetParent<Widget>
{
    public void Slap();
}

class WidgetChild<WidgetParent>
{
}

So that when I want to access the parent of WidgetChild, instead of using it this way:

WidgetParent wp = wc.parent as WidgetParent;
if(wp != null)
{
    wp.Slap();
}
else throw FakeParentException();

I want to use it this way(if I could use generics):

wc.parent.Slap();
From stackoverflow
  • I don't think there is a language mechanism that would allow you to do that.

    However, you might want to use a Factory pattern to separate the construction of the class from the class itself.

    Say, make a WidgetFactory class

    class WidgetFactory
    {
        Widget CreateWidget()
        {
            return new Widget();
        }
    }
    

    And for child classes, you'd make their factory as well. Say, a WidgetParentFactory or WidgetChildFactory or you could make a generic factory:

    class WidgetFactory<T> where T : Widget
    {
        T CreateWidget()
        {
            return new T();
        }
    }
    

    Then from the CreateWidget() method you could control class instantiation so that invalid child types can not be created.

    class WidgetFactory<T> where T : Widget
    {
        T CreateWidget()
        {
            if (/*check the type T inheritance here*/)
                return new T();
            else
                throw new Exception("Invalid inheritance");
        }
    }
    

    This should do the trick for you.

    p.s. Would you care to elaborate on why you wanted to do this?

    nullDev : Thanks for the effort, chakrit. I have elaborated the usage.
    chakrit : Ok I see now. Will edit when I got home.
  • You should be able to use the code you've got by still having the non-generic class Widget and making Widget<T> derive from it:

    public abstract class Widget
    {
    }
    
    public abstract class Widget<T> : Widget where T : Widget
    {
    }
    

    You then need to work out what belongs in the generic class and what belongs in the non-generic... from experience, this can be a tricky balancing act. Expect to go back and forth a fair amount!

  • You seem to be confusing type parameters and inheritance. This should work:

    class Widget<PType> where PType :new()
    {
        public PType parent = new PType();
    }
    
    class ParentType {}
    
    class WidgetParent : Widget<ParentType> 
    {    
        public void Slap() {Console.WriteLine("Slap"); }
    }
    
    class WidgetChild : Widget<WidgetParent>
    {
    }
    public static void RunSnippet()
    {
     WidgetChild wc = new WidgetChild();
     wc.parent.Slap();
    }
    
    Jon Skeet : I'd assumed the point was to constrain the parent type to also be a widget of some kind though.
    nullDev : @Jon: Exactly,the parent has the said constraint.
  • Use interfaces:

    interface IContainerWidget { }
    
    class Widget
    {
        private IContainerWidget Container;
    }
    
    class ContainerWidget : Widget, IContainerWidget
    {
    }
    
  • Here's my stab at organizing this.

    public interface IWidget
    {
        void Behave();
        IWidget Parent { get; }
    }
    
    public class AWidget : IWidget
    {
        IWidget IWidget.Parent { get { return this.Parent; } }
        void IWidget.Behave() { this.Slap(); }
    
        public BWidget Parent { get; set; }
        public void Slap() { Console.WriteLine("AWidget is slapped!"); }
    }
    
    public class BWidget : IWidget
    {
        IWidget IWidget.Parent { get { return this.Parent; } }
        void IWidget.Behave() { this.Pay(); }
    
        public AWidget Parent { get; set; }
        public void Pay() { Console.WriteLine("BWidget is paid!"); }
    }
    
    public class WidgetTester
    {
        public void AWidgetTestThroughIWidget()
        {
            IWidget myWidget = new AWidget() { Parent = new BWidget() };
            myWidget.Behave();
            myWidget.Parent.Behave();
        }
        public void AWidgetTest()
        {
            AWidget myWidget = new AWidget() { Parent = new BWidget() };
            myWidget.Slap();
            myWidget.Parent.Pay();
        }
    
        public void BWidgetTestThroughIWidget()
        {
            IWidget myOtherWidget = new BWidget() { Parent = new AWidget() };
            myOtherWidget.Behave();
            myOtherWidget.Parent.Behave();
        }
    
        public void BWidgetTest()
        {
            BWidget myOtherWidget = new BWidget() { Parent = new AWidget() };
            myOtherWidget.Pay();
            myOtherWidget.Parent.Slap();
        }
    }
    
  • I had a similar issue and adapted it to suit this (hopefully)

    Main Code

    public class Parent<T>
        where T : Child<T>
    {
        public Parent() { }
    
    
        public T Get()
        {
            return Activator.CreateInstance(typeof(T), new object[] { this }) as T;
        }
    }
    
    public class Child<T>
        where T : Child<T>
    {
        Parent<T> _parent;
    
        public Parent<T> Parent { get { return _parent; } }
    
        public Child(Parent<T> parent)
        {
            _parent = parent;
        }
    }
    
    
    public class ItemCollection : Parent<Item>
    {
    
    }
    
    public class Item : Child<Item>
    {
        public Item(Parent<Item> parent)
            : base(parent)
        {
        }
    }
    

    Example :

    ItemCollection col = new ItemCollection();
    Item item = col.Get();
    item.Parent.Slap();
    

0 comments:

Post a Comment