В C#, почему объект списка не может быть сохранен в переменной списка


кажется, что объект списка не может быть сохранен в переменной списка в 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>

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

14 80

14 ответов:

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

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

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

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

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

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

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

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

Edit: как отметил 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
}

С Помощью 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();

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

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

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

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

вы можете попробовать код 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);

ол (теоретически) скопируйте все содержимое 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

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

на Java все наоборот, у вас нет дженериков, и вместо этого все является списком объектов во время выполнения, и вы действительно можете заполнить любой странный объект ваш якобы-строго набранный список. Поиск "овеществленных дженериков", чтобы увидеть более широкое обсуждение проблемы java...

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

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

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

вот еще один pre-.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)

у меня:

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