Как передать List <Child>в метод с параметром List <Parent>? - PullRequest
4 голосов
/ 19 декабря 2010

Я некоторое время был в стороне от наследства и мне нужно немного помочь.

У меня есть абстрактный базовый класс Chief.Есть два наследующих класса Ship и Vehicle, которые совместно используют несколько свойств через 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 ]

8 голосов
/ 19 декабря 2010

Сначала немного объяснений. Вы не можете преобразовать List<Vehicle> в List<Chief>, потому что тогда метод Add будет иметь подпись void List<Chief>.Add(Chief item), и вы сможете хранить экземпляры класса Chief в списке, который был объявлен как содержащий Vehicle s. Это сломало бы систему типов; поэтому это не разрешено.

Самый простой способ обойти это - перейти в theVehicles.ToList<Chief>(). Тем не менее, это создаст новый список. Это имеет два значения: (1) вы будете тратить впустую память, дублируя список, и (2) если метод собирается изменить сам список (добавлять / удалять элементы или заменять элементы другими членами), вы не увидите эти изменения. в списке theVehicles. (Если объекты, на которые указывают ссылки в списке, являются единственными изменяемыми объектами, это не проблема.)

Если у вас есть контроль над методом AssignRandomProperties, рассмотрите возможность использования этой подписи:

public static void AssignRandomProperties<T>(
    PSetList routeSettings,
    IList<T> theChiefs) where T : Chief
{
    ...
}
2 голосов
/ 19 декабря 2010

Посмотрите на это:

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 не является ковариантным (и при этом он не является контравариантным).

Теперь, действительно ли ваш метод AssignRandomProperties должен принимать List<T>?Если все, что вы делаете, это итерируете его, вам не нужен List<T>, вы можете хорошо справиться с IEnumerable<T>, который реализует List<T>.Хорошо, что IEnumerable<T> не предоставляет методов для изменения списка, поэтому он ковариантен (начиная с .NET 4).Это означает, что вы можете сделать это:

IEnumerable<Chief> chiefs = ships;
0 голосов
/ 21 декабря 2010

Если ваше тело метода выглядит следующим образом:

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
    }
}
0 голосов
/ 19 декабря 2010

Вот мой любимый трюк

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

Затем вы звоните как следует

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