Невозможно преобразовать массив типа значения в объект params []

Если С# может использовать 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");
    }
}

Ответ 1

Если С# может использовать int для объекта, почему бы не int [] для объекта []?

Ваш вопрос может быть также сформулирован как "каковы правила ковариации для преобразования массивов в С#?"

Они немного сложны и разбиты несколькими интересными и неудачными способами.

Прежде всего, мы должны четко указать, что мы подразумеваем под "ковариацией". Ковариация - это свойство, что отображение сохраняет отношения. Отображение здесь "T переходит в массив из T". Связь "может быть неявно преобразована". Например:

Giraffe может быть неявно преобразован в Mammal.

Это связь между двумя типами. Теперь применим отображение к обеим сторонам отношения:

Giraffe[] можно преобразовать в Mammal[].

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

Как сокращение, вместо того, чтобы говорить, что "отображение из T в массив из T является ковариантным отображением по отношению к неявному преобразованию", мы просто говорим "массивы ковариантны" и надеемся, что остальная часть этого понята из контекста.

ОК, теперь, когда у нас есть определение вниз: Массивы с элементами ссылочного типа ковариантны в С#. Трагически это ломаная ковариация:

class Mammal {}
class Giraffe : Mammal {}
class Tiger : Mammal {}
...
Mammal[] mammals = new Giraffe[1];  

Это совершенно законно, потому что массивы элементов ссылочного типа ковариантны в С#. Но тогда это происходит во время выполнения:

mammals[0] = new Tiger();

потому что млекопитающие действительно представляют собой массив Жирафов.

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

Это мой кандидат на "худшую функцию С#", но на самом деле это работает.

Ваш вопрос: "Почему ковариантность массива не работает, когда исходный массив является массивом типа значения, а целевой массив является массивом ссылочного типа?"

Потому что эти две вещи имеют другую форму во время выполнения. Предположим, что у вас есть byte[] с десятью элементами. Фактическое хранилище, зарезервированное для элементов массива, составляет десять байтов. Предположим, вы на 64-битной машине, и у вас есть object[] с десятью элементами. Хранение в восемь раз больше!

Ясно, что вы не можете преобразовать через ссылочное преобразование ссылку на хранилище для десяти байтов для хранения десяти восьмибайтовых ссылок на байты. Дополнительные семьдесят байтов не выходят из ниоткуда; кто-то должен их выделить.

Более того: кто делает бокс? Если у вас есть массив из десяти объектов, и каждый объект является байтом, каждый из этих байтов помещается в бокс. Но байты в байтовом массиве не помещаются в бокс. Итак, когда вы делаете конверсию, кто делает бокс?

В общем случае в С# ковариантные преобразования всегда сохраняют представление. Представление "ссылки на животных" точно такое же, как представление "ссылки на Жирафа". Но представления "int" и "ссылка на объект" совершенно разные.

Ожидается, что приведение одного типа массива к другому не выделяет и не копирует огромный массив. Но мы не можем иметь ссылочный идентификатор между массивом из десяти байтов и массивом из восьмидесяти байтов, содержащим десять ссылок, и поэтому вся вещь просто делается незаконной.

Теперь вы можете сказать, что происходит, когда представления одинаковы для типов значений? На самом деле это незаконно в С#:

int[] x = new uint[10];

потому что в С# правило состоит в том, что разрешены только ковариантные преобразования массивов с использованием только ссылочных типов. Но если вы заставите его выполнить время выполнения:

int[] x = (int[])(object) new uint[10];

Тогда среда выполнения позволяет это, потому что четыре байта int и четыре байта uint имеют одинаковое представление.

Если вы хотите это лучше понять, вам, вероятно, следует прочитать всю мою серию статей о том, как ковариация и контравариантность работают в С#:

Ответ 2

В самом деле, вы не можете его преобразовать. Референтные массивы являются ковариантными; массивы значений типа не. Так; вам нужно будет использовать один из:

массив значений в коробке:

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

или вы можете использовать IList:

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

или generics:

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

Последний вариант будет моим преимуществом.