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

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

6 голосов
/ 23 марта 2009

Может кто-нибудь объяснить мне, почему в .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);

Ответы [ 5 ]

14 голосов
/ 08 июля 2009

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

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

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

И вот почему.

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

например.

  • List<A> подвергает Add(A);
  • List<B> подвергает Add(B);

Если List<B> наследует от List<A> ..., чем вы могли бы сделать List<B>.Add(A);

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

11 голосов
/ 23 марта 2009

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

7 голосов
/ 23 марта 2009

JMD ответ правильный. Для обходного пути вы можете попробовать это:

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

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

List<IShipMethod> shipMethods =
    GetForShipWeight(orderItems.Cast<IPackable>().ToList());
3 голосов
/ 31 мая 2014

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

List<IShipMethod> shipMethods = GetForShipWeight(orderItems).ConvertAll(sm => sm as IShipMethod);
2 голосов
/ 12 декабря 2012

На самом деле, вы можете решить эту проблему, сделав 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...