Вопрос

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

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

Решение

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

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

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

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

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

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

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

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

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

Foo(stringEnumerable.Cast<object>());

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

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

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

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

IEnumerable<T> 

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top