Если C # может привести int к объекту, то почему не int [] к объекту []?
Ваш вопрос также может быть сформулирован как "чем являются covariance правила преобразования массивов в C #? "
Они немного хитры и разбиты несколькими интересными и неудачными способами.
Прежде всего, мы должны четко заявить, что мы имеем в видупо "ковариации".Ковариантность - это свойство, при котором отображение сохраняет отношение .Отображение здесь "T идет в массив T".Отношение "может быть неявно преобразовано".Например:
Giraffe
может быть неявно преобразовано в Mammal
.
Это отношения между двумя типами.Теперь примените сопоставление к обеим сторонам отношения:
Giraffe[]
можно преобразовать в Mammal[]
.
Если истинность первого утверждения всегда влечет за собой истинность второго утверждения, то есть, если отображение сохраняет истинность отношений, - тогда сопоставление называется«ковариантны».
В качестве краткости вместо того, чтобы сказать «отображение из T в массив T является ковариантным отображением по неявному отношению преобразования», мы просто говорим «массивы являются ковариантными» и надеемся, что остальная часть этого понятнаиз контекста.
Хорошо, теперь, когда у нас нет определения: массивы с элементами ссылочного типа являются ковариантными в C #.К сожалению, это нарушенная ковариация:
class Mammal {}
class Giraffe : Mammal {}
class Tiger : Mammal {}
...
Mammal[] mammals = new Giraffe[1];
Это совершенно законно, потому что массивы элементов ссылочного типа ковариантны в C #.Но затем происходит сбой во время выполнения:
mammals[0] = new Tiger();
, потому что млекопитающие действительно массив жирафов .
Это означает, что каждый раз, когда вы пишете к массиву, элементами которого являются незапечатанные ссылочные типы , среда выполнения выполняет проверку типа и может произойти сбой, если проверка типа не удалась .
Это мой кандидат на "худший"особенность C # ", но на самом деле это работает .
Ваш вопрос:" почему ковариация массива не работает, когда исходный массив является массивом типа значения, а целевой массив являетсямассив ссылочного типа? "
Поскольку эти две вещи имеют разную форму во время выполнения .Предположим, у вас есть byte[]
с десятью элементами.Фактическое хранилище, зарезервированное для элементов массива, составляет десять байтов.Предположим, вы работаете на 64-битной машине и у вас есть object[]
с десятью элементами.Объем хранилища в восемь раз больше!
Очевидно, что вы не можете преобразовать посредством преобразования ссылок ссылку на хранилище для десяти байтов в хранилище для десяти восьмибайтовых ссылок на байты.Дополнительные семьдесят байтов не появляются ниоткуда;кто-то должен их выделить.
Более того: , кто делает бокс ?Если у вас есть массив из десяти объектов, и каждый объект является байтом, каждый из этих байтов имеет размер в штучной упаковке .Но байты в байтовом массиве не упакованы.Итак, когда вы делаете преобразование, кто делает бокс?
Вообще в C #, ковариантные преобразования всегда сохраняют представление .Представление «ссылка на животное» точно такое же, как представление «ссылка на жирафа».Но представления «int» и «ссылка на объект» совершенно разные.
Можно ожидать, что приведение одного типа массива к другому не выделяет и не копирует огромный массив .Но у нас не может быть ссылочного идентификатора между массивом из десяти байтов и массивом из восьмидесяти байтов, содержащим десять ссылок, и поэтому все это просто становится незаконным.
Теперь вы можете сказатьЧто ж, что происходит, когда представления одинаковы для типов значений?На самом деле это недопустимо в C #:
int[] x = new uint[10];
, потому что в C # правило состоит в том, что допустимы только преобразования ковариантных массивов, включающие только ссылочные типы.Но если вы заставите это сделать во время выполнения:
int[] x = (int[])(object) new uint[10];
Тогда среда выполнения позволяет это, потому что четыре байта int и четыре байта uint имеют одинаковое представление.
Если вы хотите лучше понять это, то вам, вероятно, следует прочитать всю мою серию статей о том, как ковариация и контравариантность работают в C #: