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

StackOverflow https://stackoverflow.com/questions/6557

Вопрос

Кажется, что объект 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>

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

Это было полезно?

Решение

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

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

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

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

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

Другие советы

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

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

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

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

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

Чтобы сделать это до 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
}

Использование Линка:

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();

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

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

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

Это во многом связано с ковариацией: например, универсальные типы считаются параметрами, и если параметры не преобразуются должным образом в более конкретный тип, операция завершается неудачей.Следствием этого является то, что вы действительно не можете выполнить приведение к более общему типу, например объекту.И, как заявил Рекс, объект 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.

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

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

Майк, я считаю, что в C# контрвариантность тоже недопустима.

Видеть Изменение параметра универсального типа в CLR для получения дополнительной информации.

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

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

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

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

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

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

Вот еще одно решение до .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;
}

(На примере Зубы)

У меня есть:

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

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

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

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

Мм, благодаря предыдущим комментариям я нашел два способа это выяснить.Первый — получить строковый список элементов, а затем преобразовать его в список объектов 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();

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

List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top