开发者

How to pass a List<Child> to a method with parameter List<Parent>?

I've been away from inheritance for a while and need a little helping hand.

I have an abstract base class Chief. There are two inheriting classes Ship and Vehicle, which share several properti开发者_如何学Pythones via Chief.

I have a method which uses those common properties:

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

but when I try to pass a

List<Ship> 

or a

List<Vehicle> 

I'm told that they can't be converted. Here's a sample method call:

ChiefRouteRandomizer.AssignRandomProperties(s.Route, theVehicles);

I was assuming of course that the List<Vehicle> would be treated as a List<Chief> when passed as the argument to AssignRandomProperties, but apparently not. Does the list need converting to List<Chief> first? If so, how?


First some explanation. You cannot convert List<Vehicle> to List<Chief> because then the Add method would have the signature void List<Chief>.Add(Chief item) and you would be able to store instances of the Chief class in a list that was declared to only hold Vehicles. This would break the type system; therefore it is not allowed.

The easiest way to get around this is to pass in theVehicles.ToList<Chief>(). However, this will create a new list. This has two implications: (1) You would be wasting memory by duplicating the list, and (2) if the method is going to mutate the list itself (add/remove items or replace members with other members) you will not see those changes on the theVehicles list. (If the objects the references in the list point to are the only things being modified, this is not a problem.)

If you have control over the AssignRandomProperties method, consider using this signature instead:

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


Look at this:

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

That's why you cannot treat a list of ships as a list of chiefs. If you want to learn more about this, it is said that List is not covariant (and neither is it contravariant).

Now, does your AssignRandomProperties method really need to take a List<T>? If all you do is iterate it, you don't need a List<T>, you can do fine with an IEnumerable<T>, which List<T> implements. The good thing here is IEnumerable<T> provides no methods for modification of the list, so it is covariant (starting with .NET 4). This means you can do this:

IEnumerable<Chief> chiefs = ships;


Here is my favorite trick

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

Then, you call like following

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


Assuming your method body looks like this:

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

and assuming that you are using C# 4.0, which introduced covariance and contravariance, you could do this, avoiding the generic solutions already suggested:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜