2008-08-09 1 views
76

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

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>

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

+5

Это будет изменяться с помощью C# 4.0, поэтому вы можете захотеть найти ковариацию и контравариантность. Это позволит таким вещам безопасным образом. – 2009-03-04 08:10:42

+0

Более или менее дублирующийся: http://stackoverflow.com/questions/317335/why-can-i-not-return-a-listfoo-if-asked-for-a-listifoo – 2009-05-20 12:25:12

ответ

35

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

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

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

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

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

12

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

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

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

6

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

ола (теоретически) скопировать все содержимое СЛ без проблем.

35

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

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

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

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

14

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

Для этого предварительно 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(); 
1

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

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

1

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

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

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

5

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

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

Это еще одно решение для 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 в)

0

У меня есть:

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

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

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

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

0

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

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

0
List<string> sl = new List<string>(); 
List<object> ol; 
ol = new List<object>(sl); 
Смежные вопросы