Невозможно неявно преобразовать тип 'IFoo & lt; double & gt;' к 'IFoo & lt; object & gt;' используя параметры ковариантного типа [дубликат]

Другой необычный подход, который работает для гетеро- и однородных списков целых чисел:

def unusual_flatten(some_list: list) -> list:
    cleaned_list = str(some_list).replace(("["), "").replace("]", "").split(",")
    return [int(item) for item in cleaned_list]

Применение в списке примеров ...

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10]

unusual_flatten(l)

Результат:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

128
задан Cuong Le 19 September 2012 в 08:26
поделиться

4 ответа

В принципе, дисперсия применяется, когда CLR может гарантировать, что ей не нужно делать какие-либо репрезентативные изменения к значениям. Все ссылки одинаковы, поэтому вы можете использовать IEnumerable<string> как IEnumerable<object> без каких-либо изменений в представлении; сам собственный код не обязательно должен знать, что вы делаете со значениями, если инфраструктура гарантировала, что он определенно будет действительным.

Для типов значений, которые не work - для обработки IEnumerable<int> как IEnumerable<object>, код, использующий последовательность, должен был бы знать, следует ли выполнять преобразование бокса или нет.

Возможно, вы захотите прочитать блог Эрика Липперта пост по представлению и идентичности для более подробной информации по этой теме в целом.

EDIT: перечитав блог в блоге Эрика, это как минимум примерно тождество как представление, хотя эти два связаны. В частности:

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

106
ответ дан Jon Skeet 25 August 2018 в 11:19
поделиться

Я думаю, что все начинается с определения LSP (Принцип замещения Лискова), который climes:

, если q (x) - свойство, доказуемое об объектах x типа T, то q ( y) должно быть истинным для объектов y типа S, где S является подтипом T.

Но типы значений, например int, не могут быть заменены object в C#. Доказательство очень просто:

int myInt = new int();
object obj1 = myInt ;
object obj2 = myInt ;
return ReferenceEquals(obj1, obj2);

Это возвращает false, даже если мы назначим ту же «ссылку» на объект.

8
ответ дан Dean Kuga 25 August 2018 в 11:19
поделиться

Он доходит до детали реализации: типы значений реализуются по-разному для ссылочных типов.

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

Самый простой способ увидеть разницу - просто рассмотреть Array: массив типов значений объединяется в память смежно (напрямую), где в качестве массива В ссылочных типах всегда имеется ссылка (указатель) в памяти; объекты, на которые указываются, выделены отдельно.

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

(*) Это может рассматриваться как одна и та же проблема. .

2
ответ дан Mark Hurd 25 August 2018 в 11:19
поделиться

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

IEnumerable<string> strings = new[] { "A", "B", "C" };

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

[0] : string reference -> "A"
[1] : string reference -> "B"
[2] : string reference -> "C"

Это набор из трех элементов, каждый являясь ссылкой на строку. Вы можете передать это в коллекцию объектов:

IEnumerable<object> objects = (IEnumerable<object>) strings;

В основном это то же представление, за исключением того, что ссылки являются ссылками на объекты:

[0] : object reference -> "A"
[1] : object reference -> "B"
[2] : object reference -> "C"

Представление такое же. Ссылки рассматриваются только по-разному; вы больше не можете получить доступ к свойству string.Length, но вы все равно можете вызвать object.GetHashCode(). Сравните это с коллекцией ints:

IEnumerable<int> ints = new[] { 1, 2, 3 };
[0] : int = 1
[1] : int = 2
[2] : int = 3

Чтобы преобразовать это в IEnumerable<object>, данные должны быть преобразованы путем бокса ints:

[0] : object reference -> 1
[1] : object reference -> 2
[2] : object reference -> 3

Это преобразование требует больше, чем литье.

8
ответ дан Martin Liversage 25 August 2018 в 11:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: