Общий список .NET Casting

Может ли кто-нибудь объяснить мне, почему в .NET 2.0, если у меня есть интерфейс IPackable и класс, реализующий этот интерфейс OrderItem, когда у меня есть метод, который принимает List<IPackable>, передача списка List<OrderItem> не работает?

Кто-нибудь знает, как я могу выполнить эту функцию?

Код:

public interface IPackable {
        double Weight{ get; }
}

public class OrderItem : IPackable


public List<IShipMethod> GetForShipWeight(List<IPackable> packages) {
   double totalWeight = 0;
   foreach (IPackable package in packages) {
        totalWeight += package.Weight;
   }
}

Следующий код не работает.

List<OrderItem> orderItems = new List<OrderItem>();
List<IShipMethod> shipMethods = GetForShipWeight(orderItems);

person Josh    schedule 23.03.2009    source источник
comment
Опубликуйте соответствующий код и укажите, какие именно проблемы у вас возникли (ошибка сборки, ошибка времени выполнения и т. Д.).   -  person Misko    schedule 23.03.2009


Ответы (5)


Эта функция называется ковариацией / контравариантностью и будет поддерживаться в C # 4.0. Вы можете прочитать об этом здесь: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

person JMD    schedule 23.03.2009
comment
Статья вроде переехала. Посмотрите ниже примеры, класс Cast ‹› находится в пространстве имен System.Linq просто FYI. - person Lucas; 07.05.2013

JMD наполовину правильный. Фактически, совершенно неверно утверждать, что мы сможем создать общий список с помощью C # 4.0. Верно, что ковариация и контравариантность будут поддерживаться в C # 4.0, но он будет работать только с интерфейсом и делегатом и будет иметь множество ограничений. Следовательно, с List работать не будет.

Причина действительно проста.

Если B является подклассом A, мы не можем сказать, что List<B> является подклассом List<A>.

И вот почему.

List<A> предоставляет некоторые методы ковариаций (возвращающие значение) и некоторые методы контравариаций (принимающие значение в качестве параметра).

e.g.

  • List<A> открывает Add(A);
  • List<B> раскрывает Add(B);

Если List<B> наследуется от _10 _..., то вы сможете сделать List<B>.Add(A);

Следовательно, вы потеряете всю безопасность типов для дженериков.

person Community    schedule 08.07.2009

Ответ JMD правильный. В качестве обходного пути вы можете попробовать следующее:

List<IPackable> orderItems = new List<IPackable>();
List<IShipMethod> shipMethods = GetForShipWeight(orderItems);

Или, если список должен быть строго типизирован как OrderItems, тогда это (только для 3.0, извините):

List<IShipMethod> shipMethods =
    GetForShipWeight(orderItems.Cast<IPackable>().ToList());
person mqp    schedule 23.03.2009

Альтернативное решение, которое также работает для .NET 3.5

List<IShipMethod> shipMethods = GetForShipWeight(orderItems).ConvertAll(sm => sm as IShipMethod);
person Bigjim    schedule 31.05.2014

Фактически, вы можете решить эту проблему, сделав GetForShipWeight универсальной функцией:

public interface IPackable { double Weight { get; } }
public interface IShipMethod { }

public class OrderItem : IPackable { }

public List<IShipMethod> GetForShipWeight<T>(List<T> packages) where T : IPackable
{
    List<IShipMethod> ship = new List<IShipMethod>();
    foreach (IPackable package in packages)
    {
        // Do something with packages to determine list of shipping methods
    }
    return ship;
}
public void Test()
{
    List<OrderItem> orderItems = new List<OrderItem>();
    // Now compiles...
    List<IShipMethod> shipMethods = GetForShipWeight(orderItems);
}
person Zarigani    schedule 11.12.2012