如何将List <child>传递给参数为List <parent>?</parent> </child>的方法

时间:2010-12-19 06:23:52

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

我已经离开遗产一段时间了,需要一点帮助。

我有一个抽象基类Chief。有两个继承类ShipVehicle,它们通过Chief共享多个属性。

我有一个使用这些常用属性的方法:

public static void AssignRandomProperties(PSetList routeSettings, List<Chief> theChiefs)

但是当我尝试传递

List<Ship> 

List<Vehicle> 

我被告知他们无法转换。这是一个示例方法调用:

ChiefRouteRandomizer.AssignRandomProperties(s.Route, theVehicles);

我当然假设List<Vehicle>在作为List<Chief>的参数传递时会被视为AssignRandomProperties,但显然不是。列表是否需要先转换为List<Chief>?如果是这样,怎么样?

4 个答案:

答案 0 :(得分:8)

首先解释一下。您无法将List<Vehicle>转换为List<Chief>,因为Add方法将具有签名void List<Chief>.Add(Chief item),您可以将Chief类的实例存储在已声明的列表中只持有Vehicle。这会破坏类型系统;因此不允许。

解决这个问题的最简单方法是传入theVehicles.ToList<Chief>()。但是,这将创建一个新列表。这有两个含义:(1)你会通过复制列表浪费内存,(2)如果方法要改变列表本身(添加/删除项目或用其他成员替换成员),你将看不到这些更改在theVehicles列表中。 (如果列表中的引用指向的对象是唯一被修改的对象,则这不是问题。)

如果您可以控制AssignRandomProperties方法,请考虑使用此签名:

public static void AssignRandomProperties<T>(
    PSetList routeSettings,
    IList<T> theChiefs) where T : Chief
{
    ...
}

答案 1 :(得分:2)

看看这个:

List<Ship> ships = new List<Ship>();
List<Chief> asChiefs = (List<Chief>)ships;
asChiefs.Add(new Car());
// so what, now ships[0] is a Car?

这就是为什么你不能把船只列表作为酋长名单。如果你想进一步了解这一点,可以说List不是covariant(也不是逆变)。

现在,您的AssignRandomProperties方法是否真的需要List<T>?如果您所做的只是迭代它,那么您不需要List<T>,您可以使用IEnumerable<T>实现的List<T>。这里的好处是IEnumerable<T>没有提供修改列表的方法,所以它是协变的(从.NET 4开始)。这意味着你可以这样做:

IEnumerable<Chief> chiefs = ships;

答案 2 :(得分:0)

这是我最喜欢的技巧

public static void AssignRandomProperties<T>(PSetList routeSettings, List<T> theChiefs) 
    where T : Chief

然后,你打电话如下

AssignRandomProperties<Vehicle>(s.Route, theVehicles);

答案 3 :(得分:0)

假设您的方法体看起来像这样:

public static void AssignRandomProperties(PSetList routeSettings, List<Chief> theChiefs)
{
    foreach (var chief in theChiefs)
    {
        //assign some properties
    }
}

并且假设您使用引入协方差和逆变的C#4.0,您可以这样做,避免使用已建议的通用解决方案:

public static void AssignRandomProperties(PSetList routeSettings, IEnumerable<Chief> theChiefs)
{
    foreach (var chief in theChiefs)
    {
        //assign some properties
    }
}