你如何使用泛型在基类上使用foreach?

时间:2009-06-03 14:30:04

标签: c# generics inheritance foreach

此问题是尝试Jon Skeet'sthis question进行回答的结果。

所以我在上述问题链接的问答中有以下代码。

 public abstract class DeliveryStrategy { }
public class ParcelDelivery : DeliveryStrategy { }
public class ShippingContainer : DeliveryStrategy { }

public abstract class Order<TDelivery> where TDelivery : DeliveryStrategy
{
    private TDelivery delivery;

    protected Order(TDelivery delivery)
    {
        this.delivery = delivery;
    }

    public TDelivery Delivery
    {
        get { return delivery; }
        set { delivery = value; }
    }
}

public class CustomerOrder : Order<ParcelDelivery>
{
    public CustomerOrder()
        : base(new ParcelDelivery())
    { }
}

public class OverseasOrder : Order<ShippingContainer>
{
    public OverseasOrder() : base(new ShippingContainer())
    {

    }
}

我正在尝试更多地了解泛型以提高我的技能,所以我有以下问题: 现在我如何使用foreach循环遍历“订单”集合?我正在使用C#2.0。

代码示例我正在尝试做什么(不编译)。

List<Order> orders = new List<Order>();

orders.Add(new CustomerOrder());
orders.Add(new CustomerOrder());
orders.Add(new OverseasOrder());
orders.Add(new OverseasOrder());

foreach (Order order in orders)
{
     order.Delivery.ToString();
}

编辑:添加了OverseasOrder以提供更好的示例。

8 个答案:

答案 0 :(得分:6)

这里的问题是订单是一个通用类,因此Order<T>Order基本上是两种不同的类型。

你可以这样做:

public abstract class Order
{
    public abstract String GetDeliveryMethod();
}

public abstract class Order<TDelivery> : Order
    where TDelivery : DeliveryStrategy
{
    .... the rest of your class

    public override String GetDeliveryMethod()
    {
        return Delivery.ToString();
    }
}

或者......你可以将Order重新定义为非泛型,在这种情况下你会失去一些强有力的输入。

答案 1 :(得分:2)

通用是你在这里出错的地方。由于泛型的类型是在编译时编写的,因此您需要指明您使用类型列出的订单类型。

如果您添加类型,它将编译:

List<Order<ParcelDelivery>> orders = new List<Order<ParcelDelivery>>();

        orders.Add(new CustomerOrder());
        orders.Add(new CustomerOrder());
        orders.Add(new CustomerOrder());
        orders.Add(new CustomerOrder());
        orders.Add(new CustomerOrder());

        foreach (Order<ParcelDelivery> order in orders)
        {
            order.Delivery.ToString();
        }

也许这不再是你想要的了,但是如果你需要能够在没有预定类型的情况下进行设置,那么你必须采用不同的方式。

答案 2 :(得分:2)

在您的情况下,没有订单类,只有订单&lt; TDelivery&gt;类。

如果您想在不知道任何特定子类型(或通用参数)的情况下处理订单,则需要将您需要的内容抽象到基类或接口中,并使用新方法对Delivery.ToString进行外观处理。订单类。

abstract class Order {
    public abstract string DeliveryString();
}

abstract class Order<TDelivery> : Order {
    // methods omitted for brevity

    public override string DeliveryString() {
        return Delivery.ToString();
    }
}

答案 3 :(得分:1)

但你没有任何Order课程......你有Order<T>课程。 这就是为什么你不能List<Order>上课。

如果您想为列表中现在可以使用的所有订单创建基类,您应该尝试这种方式:

public abstract class OrderBase{ ... }
...
public abstract class Order<TDelivery>:OrderBase where TDelivery : DeliveryStrategy { ... }

答案 4 :(得分:1)

List<Order>不是有效类型,因为Order不是有效类型 - 您需要Order<T>。除了你不知道T。

两种可能性:

选项1:将foreach包含在通用方法中:

public void WriteOrders<T>(IList<Order<T>> orders)
{
    foreach (Order<T> order in orders)
        Console.WriteLine(order.Delivery.ToString());
}

选项2:也许您不知道您实际拥有的订单类型。你真的想要一个“任何顺序”。有人猜测将此添加到C#中 - 推测的功能称为“mumble types”,如“(mumble mumble)的顺序”。但是如果没有这样的语言特性,你需要有一些具体的类,你可以创建一个列表。

因此,在您的示例中,您可以创建一个名为IOrder的接口,或一个名为Order或OrderBase的基类(可能是抽象的)。在任何一种情况下,您都不能在该基类/接口上放置Del​​ivery属性,因为您不知道Delivery将是什么类型,因此您必须向IOrder添加另一个方法或属性。例如,您可以在IOrder上放置一个DeliveryAsString属性。

答案 5 :(得分:1)

你不能只说订单,你必须说什么订单(即Order<???>) 这应该有效:

List<Order<ParcelDelivery>> orders = new List<Order<ParcelDelivery>>();

orders.Add(new CustomerOrder());
orders.Add(new CustomerOrder());
orders.Add(new CustomerOrder());
orders.Add(new CustomerOrder());
orders.Add(new CustomerOrder());

foreach (Order<ParcelDelivery> order in orders)
{
    order.Delivery.ToString();
}

然而问题是.NET不支持通用多态,所以你不能只将变量定义为Order<DeliveryStrategy>并期望它们起作用:(

答案 6 :(得分:1)

Order<T>是一个编译时抽象。 Order将是运行时抽象,但问题中没有Order

由于没有Order,因此没有List<Order>。有这些类型的集合:

List<Order<ParcelDelivery>>
List<CustomerOrder>
List<Order<ShippingContainer>>
List<OverseasOrder>
ArrayList

假设你有这个集合:

ArrayList myOrders = GetOrders();

然后你可以遍历集合并以这种方式获取CustomerOrder实例:

foreach(object someThing in myOrders)
{
  CustomerOrder myOrder = someThing as CustomerOrder;
  if (myOrder != null)
  {
    //work with myOrder
  }
}

同样,如果您有List<Order<ShippingContainer>>,可以从中获取OverseasOrder实例。

在.Net Framework 2.0中,这种技术是如何使用运行时抽象的。 在.Net Framework 3.5中,可以调用Enumerable.OfType<T>来执行过滤。

答案 7 :(得分:0)

要迭代订单,您可以像这样使用它......

orders.ForEach(delegate(Order orderItem)             {                 //所以一些处理             });

如果您不想使用匿名方法,请编写一个方法,这样做。

相关问题