如何使用C#中的通用接口从子对象引用父对象?

时间:2012-03-15 18:35:35

标签: c# .net generics .net-4.0 interface

我有以下接口声明:

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

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

我希望列表中的项目具有对标题对象的引用,因此它可以使用标题中的ID和其他字段。

在我的具体课程中,它抱怨我没有正确实施“父”。

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>并传入父类型,但这导致循环引用,因为Header类需要Item类类型...我感到困惑。

有关如何正确实施此建议的任何建议吗?

4 个答案:

答案 0 :(得分:3)

如果你这样定义你的界面:

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 :(得分:1)

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

更通用

答案 2 :(得分:1)

以下是更改界面的解决方案:

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

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

StoreOrderStoreOrderItem进行更改以支持界面更改 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; }
}

...现在正在创建StoreOrderStoreOrderItem个实例,并让它们完成自己的步伐:

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

另一个选择是废弃上面的内容并取this solution并通过添加具有所需类型的Parent属性并使用显式接口实现来改变它,以避免在调用站点进行转换,从而生成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的{​​{1}}类型参数完全没有约束 - 尝试在其位置捏造其他类型(例如IOrderItem)会导致编译错误。在没有类型约束的情况下,使用object而不是仅调用它TOrder会提示有关预期类型的​​提示。这将是我最后的编辑 - 我觉得这是我最简洁的尝试;如果你很好奇,我可以提供在接口上具有双通用约束类型的前实现,但这至少是我首选的解决方案。干杯!

答案 3 :(得分:0)

满足接口的声明:

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;
    }
}