Почему инициализаторы коллекции при повторных присвоениях не допускаются?

Я всегда думал, что это нормально работает в обоих направлениях. Затем этот тест и понял, что он не разрешен при повторных назначениях:

int[] a = {0, 2, 4, 6, 8};

работает нормально, но нет:

int [ ] a;
a = { 0, 2, 4, 6, 8 };

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

Ответы

Ответ 1

Прежде всего, позвольте получить правильные термины. Это не инициализатор коллекции. Это инициализатор массива. Инициализатор коллекции всегда следует за конструктором для типа коллекции. Инициализатор массива разрешен только в локальном или инициализаторе объявления поля или в выражении создания массива.

Вы совершенно правильно заметили, что это нечетное правило. Позвольте мне точно охарактеризовать его странность:

Предположим, что у вас есть метод M, который принимает массив int. Все это законно:

int[] x = new[] { 10, 20, 30 };
int[] y = new int[] { 10, 20, 30 };
int[] z = new int[3] { 10, 20, 30 };
M(new[] { 10, 20, 30 });
M(new int[] { 10, 20, 30 });
M(new int[3] { 10, 20, 30 });

Но

int[] q = {10, 20, 30}; // legal!
M( { 10, 20, 30 } ); // illegal!

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

Прежде чем я буду критиковать и защищать этот выбор, я хочу сказать, что в первую очередь это несоответствие является исторической катастрофой. Для этого нет веских оснований. Если бы мы могли избавиться от него, не нарушая кода, мы бы это сделали. Но мы не можем. Если бы мы снова проектировали С# с нуля, я думаю, что хорошие шансы на то, что инициализатор "одиночного" массива без "нового" не будет действительным синтаксисом.

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

Причины, по которым инициализаторы массива не должны допускаться в виде выражений:

Инициализаторы массива нарушают свойство nice, что { всегда означает введение нового блока кода. Парсер анализа ошибок в среде IDE, которая анализирует, когда вы печатаете, любит использовать фигурные скобки как удобный способ сказать, когда утверждение неполно; если вы видите:

if (x == M(
{ 
   Q(

Тогда редактору кода довольно легко догадаться, что вам не хватает )) до {. редактор предположит, что Q( является началом инструкции, и он не имеет своего конца.

Но если инициализаторы массива являются юридическими выражениями, то может быть, что отсутствует )})){} после Q.

Во-вторых, инициализаторы массивов в качестве выражений нарушают хороший принцип, что все распределения кучи имеют в них что-то новое.

Причины, по которым инициализаторы массива должны быть разрешены в полевых и локальных инициализаторах:

Помните, что инициализаторы массива были добавлены к языку в версии 1.0, прежде чем неявно набрали locals, анонимные типы или тип вывода на массивы. Еще в тот день у нас не было приятного "нового [], [10, 20, 30}" синтаксиса, поэтому без инициализаторов массива вам нужно было бы сказать:

int[] x = new int[] { 10, 20, 30 };

который кажется очень избыточным! Я понимаю, почему они хотели получить этот "новый int []" оттуда.

Когда вы говорите

int[] x = { 10, 20, 30 };

он не является синтаксически неоднозначным; синтаксический анализатор знает, что это инициализатор массива, а не начало кодового блока (в отличие от случая, о котором я упоминал выше). Также он не является неоднозначным; ясно, что инициализатор представляет собой массив из ints из контекста.

Таким образом, этот аргумент оправдывает, почему в инициализаторах массива С# 1.0 были разрешены в локальных и инициализаторах поля, но не в контекстах выражения.

Но это не тот мир, в котором мы находимся сегодня. Если бы мы разрабатывали это с нуля сегодня, у нас, вероятно, не было бы инициализаторов массива, у которых нет "нового". В настоящее время, конечно, мы понимаем, что лучшее решение:

var x = new[] { 10, 20, 30 };

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

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

Ответ 2

Лучше всего:

    int [] a;// ={1,2,3,4};
    a = new [] {1, 2, 3, 4};

В VB работает так же, как декларация, проще xD

Dim a() As Integer '={1,2,3,4}
a = {1, 2, 3, 4}