Подтвердить что ты не робот

Тип-выведение константы в С#

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

var s = "abcd";

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

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

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

Ответ 1

На самом деле я надеюсь, что Lippert появится и посмотрит на вопрос

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

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

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

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

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

const Pi = 3.14159;

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

Ответ 2

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

В Richter CLR VIA С# (стр. 177),

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

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

//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 имела какое-либо отношение к сборке А вообще - это для program.exe буквальное значение. Это, для меня, разумный способ для компилятора С# действовать - ведь Assembly A явно объявляет, что pi является константой (это означает, что значение однократно и для всех pi = 3.14), Но я рискну предположить, что 99% разработчиков С# не понимают последствий этого и могут изменить pi на 3.1415 по прихоти.

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

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

если в сборке A указано:

decimal const pi = 3.14

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

decimal const pi = 3.1415

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

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

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

Ответ 3

Я согласен с Эриком в том, что это уродливо как грех:

const var s = "abcd"

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

const s = "abcd"

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

Ответ 4

Короткий ответ заключается в том, что так говорят разработчики языка (Microsoft).

От MSDN:

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

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

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

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

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

Ответ 5

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

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

Было ли int, long и т.д. "ключевыми словами" для идентификации переменных? Нет, они просто типы данных, могут использоваться для переменных или констант.

Я думаю, что имя ключевого слова var считалось похожим на Javascript, и я считаю неуместным.

Как насчет auto? (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1705.pdf)

Ответ 6

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

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

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

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

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

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

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

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

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

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

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

Ответ 7

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

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

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

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

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

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

Ответ 8

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

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

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

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

Ответ 9

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

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

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

Ответ 10

Основная цель IMO var - разрешить анонимные типы (тип неизвестен, с var вы можете объявить переменную для ее хранения). Более распространенное использование теперь заключается в том, чтобы писать меньше кода;). Поскольку они описывают здесь, если вы знаете тип и значение (которое не изменится), просто напишите тип.

Ответ 11

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

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

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

Это не похоже на то, что в С# нет прецедента для этого. 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, не задумываясь о последствиях (с помощью ошибки компилятора). Вот почему было бы здорово, если бы он стал языковой функцией.