Почему в C # запрещены локальные переменные только для чтения?

116

Поговорите об этом с коллегой по работе. У нас есть некоторые мысли по этому поводу, но нам интересно, что думает по этому поводу SO толпа?

Брайан Генизио
источник
4
@ColonelPanic C и C ++ имеют постоянные локальные переменные, которые вы можете инициализировать с помощью значения, вычисляемого во время выполнения.
Crashworks 09
1
JavaScript 2015 (ES6) имеет тип const. Например {const myList = [1,2,3]; }. Использование этой конструкции - очень хорошая практика программирования. Дополнительная информация: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
andrew.fox
1
Для тех, кто заинтересован, есть предложение UserVoice для этой функции . В настоящее время он набрал только 87 голосов, поэтому, если вы хотите увидеть локальные переменные, доступные только для чтения, сделайте это!
Ян Кемп,
1
Это не только языковая проблема. Проблема большинства в сообществе C #, включая гуру C # с самым высоким рейтингом, заключается в том, что они не заботятся о правильности const и обо всем, что с ней связано. Сопротивление бесполезно.
Патрик Фромберг,
1
Обновление 2017 : проголосуйте за обсуждаемый запрос функции в репозитории C # Language Design! github.com/dotnet/csharplang/issues/188
Colonel Panic

Ответы:

14

Одна из причин - отсутствие поддержки CLR для локального файла только для чтения. Только чтение транслируется в код операции CLR / CLI только для инициализации. Этот флаг может применяться только к полям и не имеет значения для локального. Фактически, применение его к локальному, скорее всего, приведет к созданию непроверяемого кода.

Это не значит, что C # не может этого сделать. Но это даст два разных значения одной и той же языковой конструкции. Версия для местных жителей не будет иметь сопоставления, эквивалентного CLR.

JaredPar
источник
57
На самом деле это не имеет ничего общего с поддержкой этой функции в интерфейсе командной строки, потому что локальные переменные никоим образом не доступны для других сборок. readonlyКлючевое слово для полей должно поддерживаться CLI , поскольку его эффект является видимым для других узлов. Все это будет означать, что переменная имеет только одно назначение в методе во время компиляции.
Сэм Харвелл,
16
Думаю, вы только что переместили вопрос к тому, почему CLR не поддерживает это, а не на обоснование этого. Это действительно позволяет использовать константы для локальных переменных, поэтому было бы разумно ожидать и локальные переменные только для чтения.
Чад Шуггинс,
9
Примером этого являются переменные, определенные в операторе using. Они локальные ... и только для чтения (попробуйте назначить их, C # добавит ошибку).
Softlion
7
-1 В C ++ нет поддержки машинного кода const(который в C ++ больше похож на C #, readonlyчем на C # const, хотя он может играть обе роли). Тем не менее, C ++ поддерживает constлокальные автоматические переменные. Следовательно, отсутствие поддержки CLR для C # readonlyдля локальной переменной не имеет значения.
Приветствия и hth. - Альф,
5
1. Это может быть функция компилятора, как в C ++. Поддержка CLR совершенно неуместна. Машинная сборка тоже не поддерживает, и что? 2. (это), скорее всего, приведет к созданию непроверяемого кода - я не понимаю, как, но, возможно, я ошибаюсь. 3. это придало бы два разных значения одной и той же языковой конструкции - я сомневаюсь, что кто-то сочтет это проблемой, поскольку usingи outделают именно это, и мир не рухнул.
Лу
67

Я думаю, что это плохое суждение со стороны архитекторов C #. Модификатор readonly для локальных переменных помогает поддерживать корректность программы (как и утверждения) и потенциально может помочь компилятору оптимизировать код (по крайней мере, в случае других языков). Тот факт, что он сейчас запрещен в C #, является еще одним аргументом в пользу того, что некоторые из «функций» C # являются просто применением личного стиля кодирования его создателей.

andriej
источник
12
Я согласен с частью «спасти программиста от самого себя», но что касается помощи компилятору в оптимизации кода, я придерживаюсь мнения, что компилятор может очень хорошо узнать, изменяется ли переменная в ходе метода, и соответственно оптимизирует в любом случае. Установка флага «только для чтения» перед тем, что оптимизатор в любом случае распознает для этой цели, на самом деле не приносит пользы, но может ввести в заблуждение.
Корнелиус
1
@Cornelius Я согласен с тем, что есть мнения, что в некоторых случаях компилятор использует диаграмму потока данных для определения возможности оптимизации независимо от каких-либо ключевых слов / модификаторов. Но избавление программиста от самого себя от написания неправильного или излишне неоптимизированного кода может открыть эту возможность оптимизации для компилятора.
shuva
Разве современные компиляторы все равно не выполняют статическое одиночное присвоение? В этом случае это спорное, насколько оптимизации заинтересованные (но если компилятор поддерживает SSA , то это означает , что это также просто реализовать правопреемник однократный локальные переменные).
Дай
33

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

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

Для чего это стоит, Java имеет эту функцию (используя finalмодификатор) , и я очень редко видел его использовали другие , чем в тех случаях , когда оно имеет быть использовано , чтобы позволить переменной быть захваченными анонимного внутреннего класса - и где он находится при использовании он создает впечатление беспорядка, а не полезной информации.

Джон Скит
источник
76
Есть разница между тем, чтобы увидеть, изменена ли переменная в вашем методе визуально и компилятором . Я не вижу возражений против того, чтобы написать метод, заявив о моем намерении не изменять переменную, и чтобы компилятор уведомил меня, когда я случайно это сделаю (возможно, с опечаткой через месяц)!
A. Rex
50
С другой стороны, в F # все переменные по умолчанию доступны только для чтения, и вам нужно использовать ключевое слово mutable, если вы хотите иметь возможность их изменять. Поскольку F # - это язык .NET, я полагаю, что он выполняет описанную вами проверку во время компиляции.
Joel Mueller
2
@ A.Rex: Вопрос в том, действительно ли выгода от того, что компилятор выполняет эту проверку, стоит лишних "вздоров" при чтении кода и на самом деле не заботится об этом.
Джон Скит,
3
FWIW, Scala различает локальное readonly/ finalзначение из переменных с его valи varключевыми словами. В коде Scala локальные vals используются очень часто (и фактически предпочтительнее локальных var). Я подозреваю, что основными причинами того, что finalмодификатор не используется более часто в Java, являются а) беспорядок и б) лень.
Aaron Novstrup
4
Для локальных переменных, которые не используются в замыканиях, readonlyэто не будет слишком важно. С другой стороны, для локальных переменных, которые используются в замыканиях, readonlyво многих случаях компилятор может генерировать более эффективный код. В настоящее время, когда выполнение входит в блок, содержащий замыкание, компилятор должен создать новый объект кучи для закрытых переменных, даже если код, который использовал бы замыкание, никогда не выполняется . Если бы переменная была доступна только для чтения, код вне замыкания мог бы использовать обычную переменную; только когда создается делегат для закрытия ...
supercat
30

Команда разработчиков C # 7 вкратце обсудила предложение только для чтения locals и параметров для. Из заметок о совещании разработчиков C # от 21 января 2015 г . :

Параметры и локальные переменные могут быть захвачены лямбдами и, таким образом, доступны одновременно, но нет способа защитить их от проблем с общим взаимным состоянием: они не могут быть доступны только для чтения.

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

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

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

Обсуждение продолжается в репозитории C # Language Design. Проголосуйте, чтобы показать свою поддержку. https://github.com/dotnet/csharplang/issues/188

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

Это недосмотр разработчика языка C #. В F # есть ключевое слово val, и он основан на CLR. Нет причин, по которым C # не может иметь ту же языковую функцию.

Дерек Лян
источник
7

Я был тем коллегой, и это было неприятно! (просто шучу)

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

Лично мне нужно другое ключевое слово типа «var», например «inv» (invarient) или «rvar», чтобы избежать беспорядка. В последнее время я изучаю F # и нахожу неизменное привлекательным.

Никогда не знал, что у Java есть это.

Майк
источник
5

Мне нужны локальные переменные только для чтения так же, как мне нравятся локальные константные переменные. Но он менее приоритетен, чем другие темы.
Возможно, его приоритет является той же причиной, по которой дизайнеры C # (пока!) Не реализовали эту функцию. Но в будущих версиях должно быть легко (и иметь обратную совместимость) поддержка локальных переменных только для чтения.

brgerner
источник
2

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

Джейсон Пуньон
источник
6
Это текущее значение слова «только для чтения» в C #, но вопрос не в этом. «только для чтения» имеет английское значение, которое, кажется, имеет интуитивно понятное применение к локальной переменной: вы не можете писать в нее (после ее инициализации). Это очень похоже на значение применительно к переменным экземпляра, так почему (как я думаю, в оправдание) мы не можем применить его к локальным переменным?
Spike0xff
0

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

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

    public class ReadOnly<T>
    {
        public T Value { get; private set; }

        public ReadOnly(T pValue)
        {
            Value = pValue;
        }

        public static bool operator ==(ReadOnly<T> pReadOnlyT, T pT)
        {
            if (object.ReferenceEquals(pReadOnlyT, null))
            {
                return object.ReferenceEquals(pT, null);
            }
            return (pReadOnlyT.Value.Equals(pT));
        }

        public static bool operator !=(ReadOnly<T> pReadOnlyT, T pT)
        {
            return !(pReadOnlyT == pT);
        }
    }

Пример использования:

        var rInt = new ReadOnly<int>(5);
        if (rInt == 5)
        {
            //Int is 5 indeed
        }
        var copyValueOfInt = rInt.Value;
        //rInt.Value = 6; //Doesn't compile, setter is private

Может быть, не меньше кода, rvar rInt = 5но он работает.

Майк де Клерк
источник
Здесь это не помогает. Проблема с переменной 'var': {var five = 5 five = 6; Assert.That (five == 5)}
Мюррей
0

Вы можете объявить локальные переменные только для чтения в C #, если вы используете интерактивный компилятор C # csi:

>"C:\Program Files (x86)\MSBuild\14.0\Bin\csi.exe"
Microsoft (R) Visual C# Interactive Compiler version 1.3.1.60616
Copyright (C) Microsoft Corporation. All rights reserved.

Type "#help" for more information.
> readonly var message = "hello";
> message = "goodbye";
(1,1): error CS0191: A readonly field cannot be assigned to (except in a constructor or a variable initializer)

Вы также можете объявить локальные переменные только для чтения в .csxформате скрипта.

Полковник Паник
источник
5
Согласно сообщению об ошибке, messageэто не переменная, она скомпилирована в поле. Это не придирки, потому что различие явно существует и в интерактивном C #: int x; Console.WriteLine(x)разрешен интерактивный C # (потому что xэто поле и неявно инициализировано), но void foo() { int x; Console.WriteLine(x); }нет (потому что xэто переменная и используется до ее назначения). Кроме того, Expression<Func<int>> y = x; ((MemberExpression) y.Body).Member.MemberTypeбудет показано, что xэто действительно поле, а не локальная переменная.
Jeroen Mostert
0

С # уже имеет переменную только для чтения, хотя и с несколько другим синтаксисом:

Рассмотрим следующие строки:

var mutable = myImmutableCalculationMethod();
readonly var immutable = mutable; // not allowed in C# 8 and prior versions
return immutable;

Сравнить с:

var mutable = myImmutableCalculationMethod();
string immutable() => mutable; // allowed in C# 7
return immutable();

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

Вольфганг Гринфельд
источник
Это не «только для чтения». Это локальная функция, возвращающая захваченную переменную ... так много ненужных накладных расходов и уродливого синтаксиса, который даже не делает базовую переменную доступной только для чтения с точки зрения доступа к коду. Кроме того, «изменчивый» обычно применяется к объектам, а не к локальным переменным readonly var im = new List<string>(); im.Add("read-only variable, mutable object!");.
user2864740
-2

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

ссылка: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/const

public class SealedTest
{
    static void Main()
    {
        const int c = 707;
        Console.WriteLine("My local constant = {0}", c);
    }
}
Дерек Лян
источник
1
Нас интересует стиль JavaScript, в constкотором переменная может быть назначена только во время ее инициализации, а не стиль csharp, в constкотором могут использоваться только выражения времени компиляции. Например, вы не можете этого сделать, const object c = new object();но readonlyместный житель позволит вам это сделать.
binki
-5

Я думаю, это потому, что функция, имеющая переменную только для чтения, может никогда не быть вызвана, и, вероятно, что-то в ней выходит за рамки, и когда вам это нужно?

scottm
источник