Как мне сослаться на родителя из дочернего объекта с универсальными интерфейсами в C #? - PullRequest
5 голосов
/ 15 марта 2012

У меня есть следующие объявления интерфейса:

interface IOrder<T> where T: IOrderItem
{
     IList<T> Items { get; set; }
}

interface IOrderItem
{
     IOrder<IOrderItem> Parent { get; set; } // What do I put here?
}

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

В моих конкретных классах жалуется, что я не правильно реализую "Родитель".

class StoreOrder : IOrder<StoreOrderItem>
{
    public IList<StoreOrderItem> Items { get; set; }
}

class StoreOrderItem : IOrderItem
{       
    public StoreOrder Parent { get; set; } // This doesn't satisfy the interface
}

Я попытался установить IOrderItem как IOrderItem<T> и передать тип Parent, но это привело к циклической ссылке, поскольку класс Header запрашивает тип класса Item ... Я запутался.

Какой-нибудь совет, как правильно это реализовать?

Ответы [ 4 ]

3 голосов
/ 15 марта 2012

Если вы определяете свои интерфейсы так:

interface IOrder<T> where T : IOrderItem<T>
{
    IList<T> Items { get; set; }
}
interface IOrderItem<T> where T : IOrderItem<T>
{
    IOrder<T> Parent { get; set; }
}

Затем вы можете реализовать их так, чтобы получить ожидаемую функциональность:

class StoreOrder : IOrder<StoreOrderItem>
{
    public IList<StoreOrderItem> Items { get; set; }
}
class StoreOrderItem: IOrderItem<StoreOrderItem>
{
    public IOrder<StoreOrderItem> Parent { get; set; }
}
1 голос
/ 16 марта 2012

Вот решение для изменения интерфейсов:

interface IOrder<TOrder, TOrderItem> 
    where TOrderItem : IOrderItem<TOrder>
{
    IList<TOrderItem> Items { get; set; }
}

interface IOrderItem<TOrder>
{
    TOrder Parent { get; set; }
}

Внесение изменений в StoreOrder и StoreOrderItem для поддержки изменений интерфейса AND добавление пары свойств к каждому дляболее поздний тест:

class StoreOrder: IOrder<StoreOrder, StoreOrderItem>
{
    public DateTime Date { get; set; }
    public IList<StoreOrderItem> Items { get; set; }
}

class StoreOrderItem : IOrderItem<StoreOrder>
{
    public string ItemName { get; set; }
    public decimal ItemPrice { get; set; }
    public StoreOrder Parent { get; set; }
}

... и теперь создаем StoreOrder и StoreOrderItem экземпляры и определяем их скорость:

void Main()
{
    var so = new StoreOrder { Date = DateTime.Now };
    var item = new StoreOrderItem {
            Parent = so,
            ItemName = "Hand soap",
            ItemPrice = 2.50m };
    so.Items = new [] { item };

    Console.WriteLine(item.Parent.Date);
    Console.WriteLine(so.Items.First().ItemName);
}

... при запуске печатается:

3/16/2012 10:43:55 AM
Hand soap

Другой вариант - отказаться от вышеупомянутого и взять это решение и изменить его, добавив свойство Parent с нужным типом и используя явную реализацию интерфейса, чтобы избежать приведения к вызову при вызове.-sites, что делает для реализации StoreOrderItem что-то вроде этого:

class StoreOrderItem : IOrderItem
{
    public string ItemName { get; set; }
    public decimal ItemPrice { get; set; }
    public StoreOrder Parent { get; set; } // note: original implementation

    IOrder<IOrderItem> IOrderItem.Parent {  // explicit interface implementation
        get { return (IOrder<IOrderItem>)this.Parent; }
        set { this.Parent = (StoreOrder)value; }
    }
}

Моим любимым из вышеперечисленных является первое предложенное выше с двумя универсальными параметрами для IOrder и неограниченным универсальным параметром наIOrderItem.В предыдущей версии, которую я опубликовал и редактировал, оба интерфейса имели одинаковые два универсальных типа с одинаковыми ограничениями.Я чувствовал, что это идет немного за борт, поэтому я сократил это до вышеупомянутой реализации.Несмотря на полное отсутствие ограничений на параметр типа TOrder для IOrderItem - попытки выдумать другие типы вместо него (например, object) привели к ошибкам компиляции.Использование TOrder вместо простого вызова T дает подсказку об ожидаемом типе в отсутствие ограничения типа.Это будет мое последнее редактирование - я чувствую, что это самая краткая из моих попыток;если вам интересно, я могу предоставить предыдущую реализацию, которая имела двойные общие ограничения типа на интерфейсах, но это, по крайней мере, мое предпочтительное решение.ура!

1 голос
/ 15 марта 2012
class StoreOrder : IOrder<StoreOrderItem>
{
    public int Id { get; set; }
}

class StoreOrderItem : IOrderItem
{       
    public IOrder<IOrderItem> Parent { get; set; } // This doesn't satisfy the interface
}

Вы не можете специализироваться - IOrder<IOrderItem> является более общим, чем StoreOrder

0 голосов
/ 15 марта 2012

Декларация для удовлетворения интерфейсов:

class StoreOrder : IOrder<StoreOrderItem>
{
    // interface members
    public IList<StoreOrderItem> Items { get; set; }

    // own members
    public int Id { get; set; }
}

class StoreOrderItem : IOrderItem
{
    public IOrder<IOrderItem> Parent { get; set; }
}

Чтобы получить доступ к пользовательским членам, вам нужно разыграть:

class StoreOrderItem : IOrderItem
{
    void Test()
    {
        int id = ((StoreOrder)this.Parent).ID;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...