如何在c#中引用具有泛型接口的子对象的父对象?

时间:2022-01-26 16:53:52

I have the following interface declarations:

我有以下接口声明:

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

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

I want the items in the list to have a reference to the header object, so it can use the ID and other fields from the header.

我希望列表中的项目具有对header对象的引用,以便它可以使用来自header的ID和其他字段。

In my concrete classes, it complains that I don't implement "Parent" properly.

在我的具体类中,它会抱怨我没有正确地实现“父类”。

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

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

I tried setting up IOrderItem as IOrderItem<T> and passing in the Parent type, but that lead to circular reference since the Header class requries the Item class type... I got confused.

我尝试将IOrderItem设置为IOrderItem 并传入父类型,但这导致循环引用,因为头类请求项类类型……把我弄糊涂了。

Any advice on how to implement this properly?

关于如何正确地实现这一点有什么建议吗?

4 个解决方案

#1


3  

If you define your interfaces like so:

如果您这样定义接口:

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

You can then implement them like this to get the functionality that you expect:

然后你可以像这样实现它们来获得你期望的功能:

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

#2


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
}

You may not specialize - IOrder<IOrderItem> is more general than StoreOrder

您不能专门化- IOrder 比StoreOrder更一般

#3


1  

Here's a solution for changing the interfaces:

这里有一个改变界面的解决方案:

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

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

Making changes to StoreOrder and StoreOrderItem to support the interface changes AND adding a couple properties to each for a later test:

对StoreOrder和StoreOrderItem进行更改,以支持接口的更改,并为以后的测试分别添加几个属性:

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

...and now creating StoreOrder and StoreOrderItem instances, and putting them through their paces:

…现在创建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);
}

...when run, printed:

…运行时,输出:

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

Another option is to scrap the above and take this solution and alter it by adding the Parent property with the desired type and using explicit interface implementation to avoid casting at the call-sites, making for a StoreOrderItem implementation something like this:

另一种选择是废弃上述解决方案,通过添加父属性和所需的类型,并使用显式接口实现来避免在调用站点上强制转换,从而实现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; }
    }
}

My favorite of the above is the first proposal above with the two-generic parameters to IOrder and the unconstrained generic-parameter on IOrderItem. A previous version I had posted and have now edited had both interfaces each with the same two generic types each with the same constraints. I felt like this was going a bit overboard so I pared it back to the above implementation. Although there is a complete lack of constraints on TOrder type parameter to IOrderItem - attempts to fudge other types in its place (e.g., object) resulted in compile errors. Using TOrder instead of just calling it T provides a hint about the expected type in the absence of the type constraint. That will be my final edit - I feel it is the most succinct of my attempts; if you are curious I can provide the former implementation that had the double-generic-constrained-types on the interfaces, but this is at least my preferred this solution. cheers!

上面我最喜欢的是上面的第一个提议,其中包含IOrder的两个泛型参数和IOrderItem上的无约束泛型参数。我之前发布并编辑过的一个版本拥有两个接口,每个接口具有相同的两个通用类型,每个接口具有相同的约束。我觉得这有点过分了,所以我把它缩减到上面的实现。尽管对IOrderItem的TOrder类型参数有一个完全的缺乏约束——试图在它的位置(例如,对象)中篡改其他类型,导致编译错误。使用TOrder而不是仅仅调用T,可以在没有类型约束的情况下提供关于预期类型的提示。这将是我最后的编辑——我觉得这是我最简洁的尝试;如果您好奇,我可以提供在接口上具有双泛型限制类型的前实现,但这至少是我喜欢的这个解决方案。干杯!

#4


0  

Declaration to satisfy the interfaces:

满足接口的声明:

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

To access custom members you will have to cast:

要访问自定义成员,您必须转换:

class StoreOrderItem : IOrderItem
{
    void Test()
    {
        int id = ((StoreOrder)this.Parent).ID;
    }
}

#1


3  

If you define your interfaces like so:

如果您这样定义接口:

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

You can then implement them like this to get the functionality that you expect:

然后你可以像这样实现它们来获得你期望的功能:

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

#2


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
}

You may not specialize - IOrder<IOrderItem> is more general than StoreOrder

您不能专门化- IOrder 比StoreOrder更一般

#3


1  

Here's a solution for changing the interfaces:

这里有一个改变界面的解决方案:

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

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

Making changes to StoreOrder and StoreOrderItem to support the interface changes AND adding a couple properties to each for a later test:

对StoreOrder和StoreOrderItem进行更改,以支持接口的更改,并为以后的测试分别添加几个属性:

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

...and now creating StoreOrder and StoreOrderItem instances, and putting them through their paces:

…现在创建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);
}

...when run, printed:

…运行时,输出:

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

Another option is to scrap the above and take this solution and alter it by adding the Parent property with the desired type and using explicit interface implementation to avoid casting at the call-sites, making for a StoreOrderItem implementation something like this:

另一种选择是废弃上述解决方案,通过添加父属性和所需的类型,并使用显式接口实现来避免在调用站点上强制转换,从而实现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; }
    }
}

My favorite of the above is the first proposal above with the two-generic parameters to IOrder and the unconstrained generic-parameter on IOrderItem. A previous version I had posted and have now edited had both interfaces each with the same two generic types each with the same constraints. I felt like this was going a bit overboard so I pared it back to the above implementation. Although there is a complete lack of constraints on TOrder type parameter to IOrderItem - attempts to fudge other types in its place (e.g., object) resulted in compile errors. Using TOrder instead of just calling it T provides a hint about the expected type in the absence of the type constraint. That will be my final edit - I feel it is the most succinct of my attempts; if you are curious I can provide the former implementation that had the double-generic-constrained-types on the interfaces, but this is at least my preferred this solution. cheers!

上面我最喜欢的是上面的第一个提议,其中包含IOrder的两个泛型参数和IOrderItem上的无约束泛型参数。我之前发布并编辑过的一个版本拥有两个接口,每个接口具有相同的两个通用类型,每个接口具有相同的约束。我觉得这有点过分了,所以我把它缩减到上面的实现。尽管对IOrderItem的TOrder类型参数有一个完全的缺乏约束——试图在它的位置(例如,对象)中篡改其他类型,导致编译错误。使用TOrder而不是仅仅调用T,可以在没有类型约束的情况下提供关于预期类型的提示。这将是我最后的编辑——我觉得这是我最简洁的尝试;如果您好奇,我可以提供在接口上具有双泛型限制类型的前实现,但这至少是我喜欢的这个解决方案。干杯!

#4


0  

Declaration to satisfy the interfaces:

满足接口的声明:

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

To access custom members you will have to cast:

要访问自定义成员,您必须转换:

class StoreOrderItem : IOrderItem
{
    void Test()
    {
        int id = ((StoreOrder)this.Parent).ID;
    }
}