Приведение от IEnumerable <Object>к IEnumerable <string> - PullRequest
6 голосов
/ 04 марта 2009

Недавно я обнаружил очень удивительное поведение в c #. У меня был метод, который принимает IEnumerable<Object> в качестве параметра, и я передавал IEnumerable<string> но это невозможно. В то время как в c # все может быть выгружено в Object, почему это невозможно? Это полностью сбивает с толку меня. Пожалуйста, кто-нибудь прояснит мне этот вопрос.

Ответы [ 5 ]

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

Технический термин для этого заключается в том, что дженерики инвариантны в C # 3.0 и более ранних версиях. Начиная с C # 4.0, актерский состав работает.

Что означает инвариант, так это то, что между двумя универсальными типами нет связи только потому, что их универсальный тип параметры связаны (то есть являются подтипами или супертипами друг друга).

В вашем примере нет никакой типизации между IEnumerable<object> и IEnumerable<string>, просто потому что строка является подтипом объекта. Их просто считают двумя совершенно не связанными типами, такими как string и int (они все еще оба являются подтипами объекта, но все есть)

Существует несколько обходных путей и исключений для этой проблемы, с которой вы столкнулись.

Во-первых, вы можете привести каждую строку отдельно к объекту, если вы используете .NET 3.0, вы можете сделать это, используя метод расширения Cast<T>(). В противном случае вы можете использовать foreach и поместить результат в новую переменную нужного вам статического типа.

Во-вторых, массивы являются исключением для ссылочного типа, т. Е. Передача в виде типа string [] методу, принимающему типы объектов [], должна работать.

8 голосов
/ 04 марта 2009

Как уже отмечали другие, универсальные типы являются инвариантными. IEnumerable<T> может быть ко-вариантом, но C # в настоящее время не поддерживает указание вариантов. Ожидается, что C # 4.0 будет поддерживать варианты, так что это может быть поддержано в будущем.

Чтобы обойти это, вы можете использовать метод расширения LINQ Cast<object>(). Предполагая, что у вас есть метод с именем Foo, который принимает IEnumerable<object>>. Вы можете назвать это так,

Foo(stringEnumerable.Cast<object>());
3 голосов
/ 04 марта 2009

Самый простой способ передать IEnumerable<string> в функцию, требующую IEnumerable<object>, - это преобразовать функцию следующим образом:

public IEnumerable<object> convert<T>(IEnumerable<T> enumerable)
    {
    foreach (T o in enumerable)
        yield return o;
    }

Когда выйдет C # 4, это не будет необходимым, потому что оно будет поддерживать ковариацию и контравариантность.

0 голосов
/ 04 марта 2009

Хороший способ подумать об этом - спросить себя: «Что бы произошло, если бы ты мог это сделать?». Возьмите следующий пример:

IEnumerable<String> strings=...;
IEnumerable<Object> objects = strings; // assume this were legal

objects.Add(new Integer(5)); // what the...

Мы только что добавили целое число в список строк. Компилятор делает это, чтобы сохранить безопасность типов.

0 голосов
/ 04 марта 2009

Вы должны использовать

IEnumerable<T> 

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...