Чому C # забороняє читати лише локальні змінні?


115

Провести дружню дискусію з колегою з цього приводу. У нас є деякі думки з цього приводу, але цікаво, що думка про це думає?


4
@ColonelPanic C і C ++ мають локальні змінні, які можна ініціалізувати зі значенням, обчисленим під час виконання.
Crashworks

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 #, включаючи гуру з найвищим рейтингом, полягає в тому, що вони не переймаються правильністю споруди і будь-чим, що з цим пов'язано. Опір даремний.
Патрік Фромберг

1
Оновлення 2017 : Будь ласка, проголосуйте за запит на функцію, який обговорюється в репортажі C # Language Design! github.com/dotnet/csharplang/isissue/188
Полковник Паніка

Відповіді:


15

Однією з причин є відсутність підтримки CLR для місцевого зразка. Readonly перекладається в CLR / CLI в інітонічному коді. Цей прапор може бути застосований лише до полів і не має значення для місцевих. Насправді, застосувавши його до локального, швидше за все, вийде неперевірений код.

Це не означає, що C # не міг цього зробити. Але це дало б два різні значення одній і тій же мовній конструкції. Версія для місцевих жителів не мала б еквівалентного відображення CLR.


57
Це фактично не має нічого спільного з підтримкою CLI для функції, оскільки локальні змінні жодним чином не піддаються впливу інших збірок. readonlyКлючове слово для полів має підтримуватися CLI , оскільки його ефект є видимим для інших вузлів. Все, що це означало б, що змінна має лише одне призначення у методі під час компіляції.
Сем Харвелл

16
Я думаю, що ви просто перенесли питання на те, чому CLR не підтримує це, а не надає раціональне за цим принципом. Це дає можливість використовувати місцевих жителів, тому було б доцільно очікувати і місцевих жителів, які читають наново.
Чад Шуггінс

9
Прикладом цього є змінна, визначена у використовуваному операторі. Вони локальні ... і лише читати (спробуйте призначити їх, C # додасть помилку).
Softlion

7
-1 У C ++ відсутня підтримка машинного коду const(яка в C ++ більше схожа на C #, readonlyніж як C # const, хоча вона може грати обидві ролі). Однак C ++ підтримує constлокальну автоматичну змінну. Отже, відсутність підтримки CLR для C # readonlyдля локальної змінної - не має значення.
Ура та хт. - Альф

5
1. Це легко може бути компіляторною функцією, як у C ++. Підтримка CLR абсолютно не має значення. Збірка машини також не підтримує, і що? 2. Можливо, це створить неперевірений код - я не бачу як, але, можливо, я помиляюся. 3. Це дало б два різні значення одній і тій же мовній конструкції - я сумніваюся, хтось би сприйняв це як проблему, оскільки usingі outробимо саме так, і світ не розпався.
Лу

66

Я думаю, що це недостатньо судження з боку архітекторів C #. модифікатор readonly для локальних змінних допомагає підтримувати правильність програми (як і твердження) і потенційно може допомогти компілятору оптимізувати код (принаймні, у випадку інших мов). Те, що зараз це заборонено в C #, є ще одним аргументом, що деякі "особливості" C # - це лише примусовий стиль особистого кодування його творців.


11
Я погоджуюся з частиною "збережіть програміста від себе", але що стосується того, щоб допомогти компілятору оптимізувати код, я вважаю, що компілятор може дуже добре дізнатися, чи змінюється змінна протягом методу чи ні, і оптимізується відповідно в будь-якому випадку. Розміщення прапора "лише для читання" перед тим, що оптимізатор все-таки визнає для цієї мети, насправді не приносить користі, але потенційно вводить в оману.
Корнелій

1
@Cornelius Я погоджуюся, що існують думки, що в деяких випадках компілятор використовує діаграму потоку даних, щоб з'ясувати можливість оптимізації незалежно від будь-якого ключового слова / модифікатора. Але врятувати програміста від написання невірного та інакше зайвого неоптимізованого коду може відкрити можливість оптимізації компілятора.
shuva

Невже сучасні компілятори не виконують статичне єдине призначення? У цьому випадку це спір, що стосується оптимізацій (але якщо компілятор підтримує SSA, то це означає, що це також тривіально реалізувати локальні змінні присвоєння один раз).
Дай

33

Звертаючись до відповіді Джареда, це, ймовірно, повинно було бути функцією часу компіляції - компілятор заборонив би вам писати до змінної після первинного декларації (яка повинна містити призначення).

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

Для чого це коштує, Java має цю функцію (використовуючи finalмодифікатор) , і я дуже рідко бачив його використовували інші , ніж в тих випадках , коли воно має бути використано , щоб дозволити змінної бути захопленими анонімного внутрішнього класу - і де він знаходиться Використовується, це справляє враження скорботи, а не корисної інформації.


75
Існує різниця між переглядом того, чи змінена змінна у вашому методі за допомогою зору та компілятором . Я не бачу заперечень щодо написання методу, заявивши про свій намір не змінювати змінну, і змусив компілятор повідомити мене, коли я випадково це зробив (можливо, з помилкою помилки через місяць)!
А. Рекс

50
З іншого боку, у F # всі змінні за замовчуванням доступні лише для читання, і вам потрібно скористатися ключовим словом "mutable", якщо ви хочете мати можливість їх змінити. Оскільки F # є мовою .NET, я думаю, це робить перевірка часу компіляції, яку ви описуєте.
Джоель Мюллер

2
@ A.Rex: Справді, питання полягає в тому, чи варто користь компілятору зробити цю перевірку, чи варто зайвий "пух" при читанні коду, а насправді не піклуючись про нього.
Джон Скіт

3
FWIW, Scala відрізняє локальні readonly/ finalзначення від змінних своїми valта varключовими словами. У коді Scala локальні vals використовуються дуже часто (і насправді є кращими перед локальними vars). Я підозрюю, що основними причинами того, що finalмодифікатор не використовується частіше в Java, є: неполадка і б) лінь.
Аарон Новструп

4
Для локальних змінних, які не використовуються при закритті, це readonlyбуло б не надто важливо. З іншого боку, для локальних змінних, які використовуються в закриттях, readonlyу багатьох випадках дозволяють компілятору генерувати більш ефективний код. В даний час, коли виконання входить до блоку, який містить закриття, компілятор повинен створити новий об'єкт купи для змінних закритого типу, навіть якщо жодного коду, який би використовував закриття, ніколи не виконується . Якщо змінна була лише для читання, код за межами закриття міг би використовувати звичайну змінну; лише тоді, коли для закриття буде створений делегат ...
supercat

30

Пропозиція, що читається лише місцевими жителями та параметрами, була коротко обговорена дизайнерською командою C # 7. З конспектів дизайнерських зустрічей C # 21 січня 2015 року :

Параметри і місцеві жителі можуть захоплюватися лямбдами і тим самим отримувати доступ до них одночасно, але немає ніякого способу захистити їх від спільних взаємних державних питань: їх не можна читати тільки щойно.

Взагалі, більшість параметрів та багато місцевих жителів ніколи не призначаються для призначення після отримання їх початкового значення. Дозвіл лише начитатися на них виразно виражатиме цей намір.

Одна з проблем полягає в тому, що ця особливість може бути "привабливою неприємністю". У той час, як "правильним" робити майже завжди буде встановлення параметрів та місцевих жителів лише заново, це загрожує кодом суттєво.

Ідея частково полегшити це полягає в тому, щоб дозволити комбінацію, що читається лише для локальної змінної, було зафіксовано значення val або щось таке коротке. Більш загально, ми можемо спробувати просто придумати коротше ключове слово, ніж встановлене лише для читання, щоб висловити лише необхідність читання.

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


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

12

Це нагляд за дизайнером мови #. F # має ключове слово val і базується на CLR. Немає причини C # не мати однакової мовної функції.


7

Я був тим співробітником, і це було не дружелюбно! (жартую)

Я б не ліквідував функцію, тому що краще писати короткі методи. Це трохи схоже на те, що ви не повинні використовувати теми, тому що вони важкі. Дайте мені ножа, і дозвольте мені нести відповідальність за те, щоб не порізати себе.

Особисто мені хотілося іншого ключового слова типу "var" типу "inv" (інваріант) або "rvar", щоб уникнути безладу. Я вивчав F # пізно і знаходжу непорушну річ привабливою.

Ніколи не знав, що у Java цього є.


5

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


2

Readonly означає, що єдине місце, на яке може бути встановлена ​​змінна примірник, - це в конструкторі. При оголошенні змінної локально вона не має примірника (вона просто в області дії), і конструктор її не може торкнутися.


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 (п'ять == 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це дійсно поле, а не локальна змінна.
Єроен Мостерт

0

c # вже має varonly var, хоча і дещо інший синтаксис:

Розглянемо наступні рядки:

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();

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


Це не "лише для читання". Це локальна функція, що повертає захоплену змінну. Настільки багато непотрібних накладних та некрасивих синтаксисів, що навіть з точки зору доступу до коду не працює базову змінну лише для читання. Крім того, "змінний" зазвичай стосується об'єктів, а не локальних змінних readonly var im = new List<string>(); im.Add("read-only variable, mutable object!");.
користувач2864740

-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місцевий дозволив би зробити це.
бінкі

-5

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.