Не удается преобразовать массив типов значений в объект params [] - PullRequest
26 голосов
/ 02 февраля 2012

Если C # может привести int к объекту, почему не int [] к объекту []?

Простой пример программы:

void Main()
{
    var a = new String[]{"0", "1"};
    var b = new int[]{0, 1};

    AssertMoreThan1(a); // No Exception
    AssertMoreThan1(b); // Exception
}

static void AssertMoreThan1(params object[] v){
    if(v.Length == 1){
        throw new Exception("Too Few Parameters");
    }
}

Ответы [ 2 ]

50 голосов
/ 02 февраля 2012

Если 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 #:

8 голосов
/ 02 февраля 2012

Действительно, вы не можете преобразовать это. Массивы ссылочного типа ковариантны; массивы типа значения не . Так; вам придется использовать один из:

массив значений в штучной упаковке:

var b = new object[] {0,1};

или вы могли бы использовать IList:

static void AssertMoreThan1(IList v) {
   ... (check with .Count)
}

или дженерики:

static void AssertMoreThan1<T>(T[] v) {
   ...
}

Последним будет мое предпочтение.

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