C#が読み取り専用のローカル変数を許可しないのはなぜですか?


115

これについて同僚と友好的な議論をする。私たちはこれについていくつかの考えを持っていますが、SOの観衆はこれについてどう思いますか?


4
@ColonelPanic CおよびC ++にはconstローカル変数があり、ランタイムで計算された値で初期化できます。
Crashworks、2016年

1
JavaScript 2015(ES6)にはconstタイプがあります。例{const myList = [1,2,3]; }。この構成を使用するのは、非常に優れたプログラミング方法です。さらに詳しい情報:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
andrew.fox

1
興味のある方のために、この機能に関するUserVoiceの提案があります。現在、投票数は87票しかないため、ローカルの読み取り専用変数を表示したい場合は、それを増やしてください。
Ian Kemp

1
言語の問題だけではありません。最高評価のC#の教祖を含むC#コミュニティの過半数の問題であり、constの正確性とそれに関連するものについては気にしません。抵抗しても無駄だ。
Patrick Fromberg、2016

1
2017年の更新:C#言語設計リポジトリで議論されている機能リクエストに投票してください!github.com/dotnet/csharplang/issues/188
大佐パニック

回答:


15

1つの理由は、読み取り専用ローカルに対するCLRサポートがないことです。ReadonlyはCLR / CLI initonlyオペコードに変換されます。このフラグはフィールドにのみ適用でき、ローカルには意味がありません。実際、これをローカルに適用すると、検証できないコードが生成される可能性があります。

これは、C#がこれを実行できなかったという意味ではありません。しかし、同じ言語構造に2つの異なる意味を与えます。ローカル用のバージョンには、CLRと同等のマッピングはありません。


57
ローカル変数は他のアセンブリに公開されないため、実際には、この機能のCLIサポートとは関係ありません。readonlyフィールドのキーワードは、その効果他のアセンブリから見えるため、CLIでサポートする必要があります。つまり、変数はコンパイル時にメソッドに1つしか割り当てられません。
サムハーウェル

16
私は、CLRがその背後にある合理性を提供するのではなく、なぜこれをサポートしないのかという質問にシフトしたと思います。これはconst localsを許可するので、読み取り専用のlocalsも期待するのが妥当です。
チャドショギンズ2012

9
この例は、usingステートメントで定義された変数です。それらはローカル...および読み取り専用です(割り当てようとすると、C#はエラーを追加します)。
Softlion 2013年

7
-1 C ++では、マシンコードのサポートはありませんconst(C ++では、C#readonlyよりもC#constに似ていますが、両方の役割を果たすことができます)。しかし、C ++はconstローカル自動変数をサポートしています。したがって、readonlyローカル変数のC#に対するCLRサポートの欠如は関係ありません。
乾杯とhth。-Alf

5
1.これは、C ++のように、簡単にコンパイラー機能にすることができます。CLRサポートは完全に無関係です。機械の組み立てもそれをサポートしていないので、何ですか?2. (それは)検証不可能なコードを生成する可能性があります -方法はわかりませんが、おそらく間違っています。3. それは同じ言語構造への2つの異なる意味を与えるだろう -私は以来、誰もが問題としてこれを見ることはないだろうusingout、世界が崩壊しなかったことを正確に行っています。
ルー

66

C#アーキテクトの一部にとって、これは悪い判断だと思います。ローカル変数のreadonly修飾子は、プログラムの正確性を維持するのに役立ち(アサートのように)、コンパイラーがコードを最適化するのに役立つ可能性があります(少なくとも他の言語の場合)。現在C#で許可されていないという事実は、C#の「機能」の一部が、その作成者の個人的なコーディングスタイルの実施にすぎないという別の議論です。


11
「プログラマーを自分から救う」部分については同意しますが、コンパイラーがコードを最適化するのを助けることに関しては、変数がメソッドの過程で変化し、それに応じて最適化するかどうかコンパイラーが非常によく見つけることができるというスタンスを保持しますどちらにしても。オプティマイザがその目的のためにとにかく認識する何かの前に「読み取り専用」フラグを配置しても、実際にはメリットはありませんが、誤解を招く可能性があります。
Cornelius、

1
@Cornelius私は、場合によっては、コンパイラーがデータフロー図を使用して、キーワード/修飾子に関係なく最適化の機会を把握するという意見があることに同意します。しかし、プログラマーが自分で誤った、または不必要に最適化されていないコードを記述しないようにすると、コンパイラーにその最適化の機会が開かれる可能性があります。
shuva

とにかく、最近のコンパイラは静的単一代入を実行しませんか?その場合、最適化に関する限り、それは意味がありません(ただし、コンパイラーがSSAをサポートしている場合は、assign-onceローカル変数を実装することも簡単です)。

33

Jaredの答えに対処するには、それはおそらくコンパイル時の機能でなければなりません-コンパイラーは、最初の宣言(割り当てを含める必要があります)の後、変数への書き込みを禁止します。

これで価値を見ることはできますか?潜在的に-正直に言って、多くはありません。変数がメソッドの他の場所に割り当てられるかどうかを簡単に判断できない場合、メソッドが長すぎます。

何それの価値については、Javaは、(使用して、この機能があるfinal修飾子を)と私がきた非常に稀にそれは例以外の使用は見られない持っている変数は、匿名内部クラスで捕捉できるようにするために使用される-それはどこであります使用すると、有用な情報というよりは、散らかった印象になります。


75
変数はして方法で変更されているかどうかを見て違いがあります光景とによってコンパイラが。メソッドを書いたり、変数を変更しないようにしたり、誤って変更した場合(おそらく1か月後にタイプミスがある場合)にコンパイラーに通知したりすることに異論はありません。
A.レックス、

50
一方、F#では、すべての変数はデフォルトで読み取り専用であり、変更できるようにするには、「mutable」キーワードを使用する必要があります。F#は.NET言語であるため、説明したコンパイル時のチェックを行うと思います。
Joel Mueller、

2
@ A.Rex:問題は、コンパイラーにそのチェックを行わせることの利点が、コードを読み取るときに余分な「ふわふわ」に値するものであり、実際には気にしないかどうかです。
Jon Skeet、

3
FWIWは、Scalaは地元の区別readonly/ finalその持つ変数から値をvalしてvar、キーワード。Scalaコードでは、ローカルvalsは非常に頻繁に使用されます(実際、ローカルvars よりも優先されます)。final修飾子がJavaでより頻繁に使用されない主な理由は、a)乱雑さとb)怠惰であると思います。
アーロンノヴストラップ2014年

4
クロージャで使用されていないローカル変数の場合、readonlyあまり重要ではありません。一方、クロージャで使用されるローカル変数のreadonly場合、多くの場合、コンパイラにより効率的なコードが生成されます。現在、クロージャーを含むブロックに実行が入ると、クロージャーを使用するコードが実行されない場合でも、コンパイラーはクローズされた変数の新しいヒープオブジェクトを作成する必要があります。変数が読み取り専用の場合、クロージャーの外側のコードは通常の変数を使用できます。クロージャのデリゲートが作成された場合のみ
スーパーキャット2015年

30

読み取り専用のローカルとパラメーターの提案は、C#7設計チームによって簡単に議論されました。2015年1月21日のためのC#デザインミーティングノート

パラメータとローカルはラムダによってキャプチャされ、それによって同時にアクセスできますが、共有相互状態の問題からそれらを保護する方法はありません。読み取り専用にすることはできません。

一般に、ほとんどのパラメーターと多くのローカル変数は、初期値を取得した後に割り当てられることは決してありません。それらに読み取り専用を許可すると、その意図が明確に表現されます。

1つの問題は、この機能が「魅力的な迷惑」になる可能性があることです。ほとんどの場合、「正しいこと」はパラメータとローカルを読み取り専用にすることですが、そうすることでコードが乱雑になります。

これを部分的に緩和するアイデアは、ローカル変数のreadonly varの組み合わせをvalまたはそのような短いものに縮小できるようにすることです。より一般的には、確立された読み取り専用よりも短いキーワードを単純に考えて、読み取り専用性を表現することができます。

C#言語設計リポジトリで議論が続けられています。投票して支持を表明しましょう。https://github.com/dotnet/csharplang/issues/188


読み取り専用をデフォルトにすることもできます(オプションやキーワードなどを使用して)。ほとんどの変数とパラメーターは読み取り専用で、書き込み可能はごくわずかです。書き込み可能な数を最小限にすることは、一般的に良いことです。
Dave Cousineau

12

これはc#言語デザイナーの見落としです。F#にはvalキーワードがあり、CLRに基づいています。C#で同じ言語機能を使用できない理由はありません。


7

私はその同僚であり、友好的ではありませんでした!(冗談だ)

短いメソッドを書く方が良いので、この機能を削除しません。スレッドは難しいので、スレッドを使用するべきではないと言うのに少し似ています。ナイフをください。自分で切らないようにしてください。

個人的には、混乱を避けるために、 "inv"(不変)または "rvar"のような別の "var"タイプのキーワードが必要でした。私は最近F#を研究していて、不変のものが魅力的であることに気づきました。

Javaがこれを持っていることを知りませんでした。


5

ローカルのconst変数と同じように、ローカルの読み取り専用変数が必要です。ただし、他のトピックよりも優先度は低くなります。 多分その優先順位は、C#デザイナーがこの機能を(まだ!)実装しないのと同じ理由です。ただし、将来のバージョンでローカルの読み取り専用変数をサポートするのは簡単(かつ下位互換性)でなければなりません。


2

Readonlyは、インスタンス変数を設定できる唯一の場所がコンストラクター内であることを意味します。変数をローカルに宣言する場合、インスタンスはなく(スコープ内にあるだけ)、コンストラクターから変更することはできません。


6
これがC#の「読み取り専用」の現在の意味ですが、それは問題ではありません。「読み取り専用」は、ローカル変数に直感的に適用できるように見える英語の意味を持っています:(初期化された後では)それに書き込むことはできません。これはインスタンス変数に適用したときの意味とよく似ているので、なぜ(正当化のように)ローカル変数に適用できないのですか?
Spike0xff 2015年

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

c#には、多少異なる構文ではありますが、すでに読み取り専用の変数があります。

次の行を検討してください。

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!");
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
const変数が初期化中にのみ割り当てられるJavaScriptスタイルに興味がありますconst。コンパイル時の式のみが使用されるcsharpスタイルには興味がありません。たとえば、あなたがすることはできませんconst object c = new object();が、readonlyローカルはこれを行うことができます。
binki

-5

これは、読み取り専用変数を持つ関数が呼び出されない可能性があるためだと思います。おそらく、その関数がスコープ外に出てしまうことがあり、いつ必要になるのでしょうか。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.