Общий родитель для общего класса

Я использую вымышленный пример для этого. Скажем, у меня есть класс Widget, например:

abstract class Widget
{
Widget parent;
}

Теперь мои другие классы будут производными от этого класса виджета, но предположим, что я хочу поместить в класс некоторое ограничение при определении производных типов, чтобы только определенный «тип» виджета мог быть родительским для определенного типа виджета.

Например, я создал еще два виджета из класса Widget, WidgetParent и WidgetChild. При определении дочернего класса я хочу определить тип родителя как WidgetParent, чтобы мне не приходилось каждый раз приводить родителя при его использовании.

А именно то, что я хотел бы сделать, это:

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

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

class WidgetChild<WidgetParent>
{
}

Так что, когда я хочу получить доступ к родителю WidgetChild, вместо того, чтобы использовать его следующим образом:

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

Я хочу использовать его таким образом (если бы я мог использовать дженерики):

wc.parent.Slap();

person nullDev    schedule 04.11.2008    source источник


Ответы (6)


Вы должны иметь возможность использовать полученный код, по-прежнему имея необщий класс Widget и производя от него Widget<T>:

public abstract class Widget
{
}

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

Затем вам нужно решить, что относится к универсальному классу, а что к неуниверсальному... по опыту, это может быть сложным балансированием. Ожидайте вернуться туда и обратно изрядное количество!

person Jon Skeet    schedule 04.11.2008

Используйте интерфейсы:

interface IContainerWidget { }

class Widget
{
    private IContainerWidget Container;
}

class ContainerWidget : Widget, IContainerWidget
{
}
person Lasse V. Karlsen    schedule 04.11.2008

Я не думаю, что существует языковой механизм, который позволил бы вам это сделать.

Однако вы можете использовать фабричный шаблон для разделения конструкции класса из самого класса.

Скажем, создайте класс WidgetFactory

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

А для детских классов вы бы тоже сделали их фабрику. Скажем, WidgetParentFactory или WidgetChildFactory, или вы можете создать универсальную фабрику:

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

Затем из метода CreateWidget() вы можете управлять созданием экземпляра класса, чтобы не могли быть созданы недопустимые дочерние типы.

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

Это должно помочь вам.

p.s. Не могли бы вы уточнить, почему вы хотели это сделать?

person chakrit    schedule 04.11.2008
comment
Спасибо за усилия, Чакрит. Я разработал использование. - person nullDev; 04.11.2008

Вы, кажется, путаете параметры типа и наследование. Это должно работать:

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();
}
person James Curran    schedule 04.11.2008
comment
Я предположил, что смысл заключался в том, чтобы ограничить родительский тип также каким-то виджетом. - person Jon Skeet; 04.11.2008
comment
@Jon: Точно, у родителя есть указанное ограничение. - person nullDev; 04.11.2008

Вот моя попытка организовать это.

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();
    }
}
person Amy B    schedule 06.11.2008

У меня была аналогичная проблема, и я адаптировал ее для этого (надеюсь)

Основной код

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

Пример:

ItemCollection col = new ItemCollection();
Item item = col.Get();
item.Parent.Slap();
person Tim Jarvis    schedule 09.01.2009