views:

462

answers:

6

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();
A: 

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?

chakrit
Thanks for the effort, chakrit. I have elaborated the usage.
nullDev
Ok I see now. Will edit when I got home.
chakrit
+2  A: 

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!

Jon Skeet
A: 

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();
}
James Curran
I'd assumed the point was to constrain the parent type to also be a widget of some kind though.
Jon Skeet
@Jon: Exactly,the parent has the said constraint.
nullDev
+1  A: 

Use interfaces:

interface IContainerWidget { }

class Widget
{
    private IContainerWidget Container;
}

class ContainerWidget : Widget, IContainerWidget
{
}
Lasse V. Karlsen
A: 

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();
    }
}
David B
A: 

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();
Tim Jarvis