Введите вывод константы в C # - PullRequest
38 голосов
/ 24 января 2010

В C # работает следующий вывод типа:

var s = "abcd";

Но почему тип не может быть выведен, когда переменная является константой?

Следующее создает исключение времени компиляции:

const var s = "abcd"; // <= Compile time error: 
                      //    Implicitly-typed local variables cannot be constant

Ответы [ 11 ]

32 голосов
/ 24 января 2010

Я на самом деле надеюсь, что Липперт заскочит и взглянет на вопрос

Если есть что-то, что вы хотите, чтобы привлечь мое внимание, вы можете оставить мое имя в тексте - не комментарий - и я найду это в конце концов. Или, лучше, вы можете «чирикать» на @ericlippert. Обратите внимание, что это не является соглашением об уровне обслуживания; Я делаю это в свободное время.

почему тип не может быть выведен, когда переменная является константой?

«константа» и «переменная» являются противоположностями . const var дает мне дрожь, чтобы напечатать. Константа - это значение, которое никогда не изменяется и не имеет места хранения; переменная - это место хранения, содержимое которого изменяется. Они совершенно разные, поэтому не пытайтесь их объединять. Синтаксис var был выбран для вызова «это переменная», и мы придерживаемся ее.

var может заменить определенное объявление типа, но объединение его с const сильно запутывает картину того, что компилятор делает со значением. Поэтому const var не допускается для предотвращения этой путаницы, и вы должны явно вводить свои константы.

Я бы прекрасно подошел к выводимым константам, которые не используют var:

const Pi = 3.14159;

мне кажется нормальным. Однако я не знаю никаких планов добавить это в C #.

12 голосов
/ 24 января 2010

Это всего лишь предположение, но я думаю, что причина, возможно, связана с тем фактом, что значения const помещаются в метаданные (которые имеют незначительные последствия, все по своему) при компиляции. Интересно, может быть, у компилятора возникли проблемы с выяснением того, как преобразовать переменную в метаданные.

В книге Рихтера CLR VIA C # (стр. 177),

Определение постоянной причины создания метаданных. Когда код относится к константный символ, компиляторы смотрят вверх этот символ в метаданных сборка, которая определяет эту константу, извлечь значение константы, и вставлять значение в испускаемый IL Код.

Далее он отмечает, что это означает, что по этой причине вы не можете получить ссылку на постоянную память. Чтобы сделать это немного более явным, в psuedo C #, если сборка A определяет const:

//Assembly A, Class Widget defines this:
public static const System.Decimal Pi = 3.14

тогда у вас есть потребитель A:

//somewhere in the Program.exe assembly
decimal myCircleCurcum = 2 * Widget.pi

результирующий скомпилированный IL из program.exe будет делать что-то вроде этого псевдокода:

// pseudo-IL just to illustrate what would happen to the const
myCircleCurcum = 2*3.14

обратите внимание, что сборка, потребляющая , даже не подозревает, что десятичная дробь 3.14 вообще имела какое-либо отношение к сборке A - это для program.exe буквальное значение . Для меня это разумный способ для компилятора C # - в конце концов, сборка A явно объявила , что pi является константой (это означает, что значение раз и навсегда pi = 3.14). Но я рискну предположить, что 99% разработчиков на C # не понимают последствий этого и могут изменить пи на 3.1415 по прихоти.

У констант действительно плохая история версий кросс-сборки (опять же, это исходит от Рихтера), потому что потребитель сборки A с константой в ней не увидит изменения, если константа сборки A изменится (то есть перекомпилирована). Это может привести к тому, что потребителю сборки А. будет очень сложно выяснить ошибки. , настолько, что я запретил моей команде использовать константы. Их небольшой прирост производительности не стоит тех тонких ошибок, которые они могут вызвать.

На самом деле вы можете использовать константу только в том случае, если знаете, что значение никогда не изменится, никогда не изменится - и даже если что-то установлено как константа, например, пи, вы не можете точно сказать, что Вы не хотите, чтобы ваше разрешение изменилось в будущем.

если сборка А определяет:

decimal const pi = 3.14

затем вы создаете его, а затем другие сборки используют его, если вы затем измените сборку A:

decimal const pi = 3.1415

и перестройте сборку A, потребитель сборки A все равно будет иметь старое значение 3.14! Зачем? потому что исходный 3.14 был определен как константа, что означает, что потребителям сборки A сказали, что значение не изменится, - поэтому они могут запекать это значение pi в свои метаданные (если вы перестраиваете потребителя сборки A, то это затем получит новое значение пи в своих метаданных). Опять же, я не рассматриваю это как проблему с тем, как CSC обрабатывает константы - просто разработчики, вероятно, не ожидают, что константа не может быть безопасно изменена при некоторых обстоятельствах, где она может быть безопасно изменены в других. Безопасно: потребители никогда не получат ссылки только по .dll (т.е. они всегда будут собираться из исходного кода КАЖДЫЙ ВРЕМЯ), небезопасно: потребители не имеют представления о том, когда исходный код вашей сборки с const определил его, как он изменяется. В документации .NET, вероятно, следует сделать гораздо более ясным, что константа означает, что вы не можете изменить значение в исходном коде

По этой причине я настоятельно рекомендую не использовать константы, а просто сделать виджет доступным только для чтения. Сколько значений, которые вы действительно можете сказать, наверняка будут постоянными во веки веков?

Единственная реальная причина использовать const over readonly в моем уме, если что-то может повлиять на производительность ... но если вы столкнетесь с этим, я бы подумал, действительно ли C # является правильным языком для вашей проблемы. Короче говоря, для меня почти никогда не стоит использовать константы. Есть очень мало случаев, когда крошечное улучшение стоит потенциальных проблем.

11 голосов
/ 10 апреля 2010

Я согласен с Эриком, что это безобразно, как грех:

const var s = "abcd"

Но почему бы не просто это?

const s = "abcd"

Мне кажется, разумный синтаксис.

7 голосов
/ 24 января 2010

Короткий ответ: так говорят разработчики языка (Microsoft).

С MSDN :

Ошибка компилятора CS0822

Сообщение об ошибке : неявно типизированные местные жители не может быть постоянным

Неявно типизированные локальные переменные необходимо только для хранения анонимного типы. Во всех остальных случаях они просто удобство. Если значение переменная никогда не меняется, просто дайте это явный тип. Попытка использовать только для чтения модификатор с неявно типизированный локальный будет генерировать CS0106.

Чтобы исправить эту ошибку

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

3 голосов
/ 24 мая 2012

Мой ответ? Поскольку в настоящее время невозможно использовать «const var», не беспокойтесь об этом. Это ограничение, без всякой причины, делает C # несбалансированным в том, как он обрабатывает константы по сравнению с переменными, и это создает асимметрию. Тебе будет лучше

«Синтаксис« var »был выбран для вызова« это переменная », и мы придерживаемся этого».

Я считаю аргумент Эрика Липперта глубоко неубедительным на нескольких уровнях.

Эрик, я не знаю, кто такие «мы», и я действительно не хочу звучать грубо, но оба используют (как в причине), И означает (как в том, почему var - подходящее имя) не имеет никакого отношения к значению, которое вы пытаетесь придать ему. «Var» - это , используемый , это место явного объявления типа и означает тот факт, что его тип на тот момент может быть одним из многих.

Напомним, что var заменяет объявление типа. Давайте не будем притворяться, что он делает что-то еще, потому что тип и значение (и может ли это значение быть изменено) - это две разные вещи. Здесь применяется бритва Оккума, и нет необходимости расширять значение var за пределы того, что он делает.

Что еще более важно, даже в те дни, когда неявные объявления были недоступны, а ключевое слово var использовалось, люди по-прежнему считали свои объекты переменными и без проблем объявляли свои переменные константами.

"var" был введен, потому что в этом была необходимость. И это не должно было сделать переменные безопасными от превращения в константы. Эта ограниченная интерпретация создает еще одну потребность, которая в настоящее время не удовлетворяется.

Вся ваша позиция может быть выведена из аргумента о симантике - нам просто не нравится, как «звучит const var» (например, «дает мне дрожь»). Это странно, учитывая, что можно напечатать что-то вроде « динамическая статика "без ошибок компиляции и что звучит тоже неловко.

Sp. Почему вы подчеркиваете что-то, что вообще не рискует быть амбициозным? Является ли "const var =" Hello World "" или какой-либо его вариант действительно причиной того, что люди будут озадачены, погода постоянная или нет. Я думаю, что люди смогут точно понять, что это значит, так же, как они понимают, что означает «динамическая статика».

Суть в том, что возможность неявного объявления констант имеет смысл и может быть полезной. В настоящее время нет способа сделать это, казалось бы, без причины. И гораздо полезнее иметь возможность объявлять «const var», чем вводить еще одно ключевое слово для обслуживания неявно объявленных констант.

И если вы не думаете, что аргумент Эрика полностью основан на излишне сложной интерпретации семантики, попробуйте построить тот же аргумент вокруг , означающего слова «var», если он вызывается с другим именем. Скажи, вкл. Будет ли какая-либо причина, по которой impl нельзя использовать вместе с const? Я был бы в затруднении, чтобы придумать единственную причину для этого. Таким образом, все сводится к тому, что вам не нравится, как звучит "const var", и больше ничего. Я думаю, что большинство из нас может легко преодолеть это.

3 голосов
/ 21 октября 2011

Я не согласен с @ Eric.

Ключевое слово var не означает «это переменная», оно означает «тип должен быть выведен».

Были ли int, long и т. Д. "Ключевыми словами" для определения переменных?Нет, это просто типы данных, которые можно использовать для переменных или констант.

Я думаю, что имя ключевого слова var считалось похожим на Javascript, и я считаю неуместным.1012 * Как насчет авто ?(http://www.open -std.org / jtc1 / sc22 / wg21 / docs /apers / 2004 / n1705.pdf )

2 голосов
/ 25 января 2013

Хотя я не согласен с рассуждениями г-на Липперта, есть веская причина не разрешать неявную типизацию именованных констант: рассмотрите значение следующего кода, если типизированные константы не должны были явно указывать их тип:

const var ScaleFactor = 2500000000; // Type 'Int64'

...
int thisValue = getNextInt();
total += thisValue * ScaleFactor;

Теперь предположим, что масштабный коэффициент нужно снизить на 20%.Каков будет эффект изменения значения на 2000000000?Хотя проблема того, чтобы Int64 стала Int32, возникла бы, даже если бы значение было указано в коде [например, при изменении total += thisValue * 2500000000; на total += thisValue * 2000000000;, изменение было бы смежным с кодом, который требует, чтобы значение было Int64,В отличие от этого, объявление const, вероятно, было бы далеко от кода, на который оно влияет, поэтому не было бы видимого способа узнать, может ли код где-то полагаться на константу, являющуюся длинным типом.

2 голосов
/ 24 января 2010

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

Другими словами, поскольку тип, очевидно, является константным и известным, нет абсолютно никакой причины использовать var.

Неявно типизированные локальные переменные необходимо только для хранения анонимного типы. Во всех остальных случаях они просто удобство. Если значение переменная никогда не меняется, просто дайте это явный тип. Попытка использовать только для чтения модификатор с неявно типизированный локальный будет генерировать CS0106.

http://msdn.microsoft.com/en-us/library/bb310881.aspx

Ошибка компилятора CS0822

Чтобы исправить эту ошибку, если вам требуется переменная должна быть постоянной или только для чтения, дайте ему явный тип.

1 голос
/ 24 января 2010

Интересно. Я не знаю, является ли это просто ограничением компилятора C # или же это фундаментальное ограничение самого языка.

Чтобы объяснить, что я имею в виду, рассмотрим В.Б.

В VB 9 вы также не могли вывести константы, но это было только ограничением компилятора. В VB 10 они смогли добавить константный вывод типа, не внося существенных изменений в язык.

0 голосов
/ 21 октября 2016

Я понял, что я (мы) на самом деле хотел, чтобы поведение ключевого слова const было определено в JavaScript или C. То есть, возможность вычислять его во время выполнения, но запрещать его обновление в последующем коде. Это может быть полезно для навязывания дисциплины и быть явным, когда вы хотите, чтобы значение вычислялось только один раз.

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

// This variable should never be overwritten!
readonly var target = 4;

Не похоже, что в C # нет прецедента для этого. using() и итерационные (foreach) переменные уже ведут себя так:

class Program
{
    static void Main(string[] args)
    {
        foreach (var x in new[] { "asdf", })
        {
            System.Console.WriteLine(x);
            // error CS1656: Cannot assign to 'x' because it is a 'foreach iteration variable'
            x = "food";
        }
    }
}

О, смотри - у меня есть вывод типа и readonly поведение! Ура! Однако использование ключевого слова foreach слишком неуклюже, чтобы фактически сделать это в реальном коде. Совершенно не очевидно, что вы пытаетесь защитить себя от того, что вы или ваши коллеги добавили код, который изменяет x позже, не продумывая последствия (с помощью ошибки компилятора). Вот почему было бы здорово, если бы он стал языковой особенностью .

...