Почему в C # нельзя хранить объект List <string> в переменной List <object>

c# .net generics covariance type-safety

72678 просмотра

14 ответа

69556 Репутация автора

Кажется, что объект List не может быть сохранен в переменной List в C # и даже не может быть явно приведен таким образом.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

приводит к невозможности неявного преобразования типа System.Collections.Generic.List<string>вSystem.Collections.Generic.List<object>

А потом...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

результаты не могут преобразовать тип System.Collections.Generic.List<string>вSystem.Collections.Generic.List<object>

Конечно, вы можете сделать это, вытащив все из списка строк и поместив его обратно по одному, но это довольно запутанное решение.

Автор: Matt Sheppard Источник Размещён: 09.08.2008 02:32

Ответы (14)


11 плюса

122774 Репутация автора

Причина в том, что обобщенный класс like List<>в большинстве случаев рассматривается как обычный класс. например, когда вы говорите, List<string>()что компилятор говорит ListString()(который содержит строки). [Технический фолк: это очень простая и понятная версия того, что происходит]

Следовательно, очевидно, что компилятор не может быть достаточно умным, чтобы преобразовать ListString в ListObject путем приведения элементов его внутренней коллекции.

Вот почему есть методы расширения для IEnumerable, такие как Convert (), которые позволяют легко предоставлять преобразование для элементов, хранящихся в коллекции, что может быть так же просто, как приведение к одному.

Автор: Rex M Размещён: 09.08.2008 02:38

37 плюса

33439 Репутация автора

Решение

Подумайте об этом так: если вы выполняете такое приведение, а затем добавляете объект типа Foo в список, список строк больше не является согласованным. Если бы вы перебрали первую ссылку, вы бы получили исключение приведения класса, потому что, как только вы нажмете экземпляр Foo, Foo не сможет быть преобразован в строку!

Как примечание стороны, я думаю, что было бы более важно, можете ли вы сделать обратное приведение:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

Я не использовал C # некоторое время, поэтому я не знаю, является ли это законным, но этот тип приведения действительно (потенциально) полезен. В этом случае вы переходите от более общего класса (объекта) к более конкретному классу (строке), который расширяется от общего. Таким образом, если вы добавляете в список строк, вы не нарушаете список объектов.

Кто-нибудь знает или может проверить, является ли такое приведение законным в C #?

Автор: Mike Stone Размещён: 09.08.2008 02:50

3 плюса

69556 Репутация автора

Майк - я считаю, что контравариантность не разрешена в C #

Посмотрите Универсальный параметр параметра дисперсия в CLR для получения дополнительной информации.

Автор: Matt Sheppard Размещён: 09.08.2008 02:53

6 плюса

81525 Репутация автора

Это во многом связано с ковариацией, например, универсальные типы рассматриваются как параметры, и если параметры не разрешаются должным образом для более конкретного типа, то операция завершается неудачей. Смысл этого в том, что вы действительно не можете привести к более общему типу, подобному объекту. И, как утверждает Рекс, объект List не будет преобразовывать каждый объект для вас.

Вы можете попробовать вместо этого код FF:

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

или же:

List<object> ol = new List<object>();
ol.AddRange(sl);

ol (теоретически) скопирует все содержимое sl без проблем.

Автор: Jon Limjap Размещён: 09.08.2008 02:58

36 плюса

36721 Репутация автора

Если вы используете .NET 3.5, взгляните на метод Enumerable.Cast. Это метод расширения, поэтому вы можете вызывать его прямо в списке.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

Это не совсем то, что вы просили, но должны сделать свое дело.

Редактировать: Как отметил Zooba, вы можете вызвать ol.ToList (), чтобы получить список

Автор: Ray Размещён: 09.08.2008 03:07

14 плюса

9765 Репутация автора

Вы не можете приводить между общими типами с различными параметрами типа. Специализированные универсальные типы не являются частью одного и того же дерева наследования и, следовательно, являются несвязанными типами.

Для этого предварительно NET 3.5:

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}

Используя Linq:

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();
Автор: Zooba Размещён: 10.08.2008 11:54

1 плюс

1178 Репутация автора

Это на самом деле так, что вы не пытаетесь поместить какой-либо нечетный «объект» в ваш вариант списка «ол» (как это List<object>может показаться разрешающим) - потому что ваш код будет аварийно завершаться (потому что список действительно есть List<string>и будет принимать только объекты типа String ). Вот почему вы не можете привести свою переменную к более общей спецификации.

В Java все наоборот, у вас нет универсальных шаблонов, и вместо этого все является списком объектов во время выполнения, и вы действительно можете вставить любой странный объект в ваш предположительно строго типизированный список. Ищите «Reified generics», чтобы увидеть более широкое обсуждение проблемы Java ...

Автор: Valters Vingolds Размещён: 10.08.2008 02:35

1 плюс

21879 Репутация автора

Такая ковариация в обобщениях не поддерживается, но вы можете сделать это с массивами:

object[] a = new string[] {"spam", "eggs"};

C # выполняет проверки во время выполнения, чтобы помешать вам, скажем, поместить intв a.

Автор: Constantin Размещён: 09.10.2008 09:54

5 плюса

85876 Репутация автора

Да, вы можете из .NET 3.5:

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();
Автор: Tamas Czinege Размещён: 18.10.2008 08:26

2 плюса

156 Репутация автора

Я думаю, что это (противоположность) будет фактически поддерживаться в C # 4.0. http://blogs.msdn.com/charlie/archive/2008/10/27/linq-farm-covariance-and-contravariance-in-visual-studio-2010.aspx

Автор: MattValerio Размещён: 07.11.2008 07:01

0 плюса

409 Репутация автора

Вот еще одно решение до .NET 3.5 для любого IList, содержимое которого может быть явным образом приведено.

public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
    List<B> newList = new List<B>();

    foreach (D item in list)
    {
        newList.Add(item);
    }

    return newList;
}

(На примере Zooba)

Автор: Alvin Ashcraft Размещён: 08.06.2009 04:34

0 плюса

1 Репутация автора

У меня есть:

private List<Leerling> Leerlingen = new List<Leerling>();

И я собирался заполнить его данными, собранными в том, List<object> что, наконец, сработало для меня, было вот этим:

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Castэто типа вы хотите получить IEnumerableот этого типа, то типаж IEnemuerableк List<>вы хотите.

Автор: Menno Размещён: 15.10.2009 09:41

0 плюса

53 Репутация автора

Мм, благодаря предыдущим комментариям я нашел два способа это выяснить. Первый - получение списка строк элементов и затем приведение его к списку объектов IEnumerable:

IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

И второй - избегать типа объекта IEnumerable, просто приводя строку к типу объекта, а затем используя функцию «toList ()» в том же предложении:

List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

Мне больше нравится второй способ. Надеюсь, это поможет.

Автор: Cristinadeveloper Размещён: 17.03.2016 09:38

0 плюса

1 Репутация автора

List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
Автор: Kozen Размещён: 27.04.2017 09:52
Вопросы из категории :
32x32