C#でフィールドを「読み取り専用」としてマークする利点は何ですか?


295

メンバー変数を読み取り専用として宣言する利点は何ですか?クラスのライフサイクル中に誰かが値を変更するのを防ぐだけですか、このキーワードを使用すると速度や効率が向上しますか?


8
良い外部回答:dotnetperls.com/readonly
OneWorld

3
面白い。これは本質的に、このJavaの質問のC#の同等であるstackoverflow.com/questions/137868/... ...はるかに少ないのに...うーん加熱され、ここでの議論
RAY

7
これは、ことは注目に値するかもしれreadonly構造タイプのフィールドは、任意のメンバーの呼び出し以来、単純に変異していない可変フィールドと比較してパフォーマンスのペナルティを課すreadonlyフィールドのコピーを作成するために、コンパイラが発生します値型のフィールドと呼び出しをその上のメンバー。
スーパーキャット2012年

2
より多くのパフォーマンスの低下に:codeblog.jonskeet.uk/2014/07/16/...
CADはやつ

回答:


171

このreadonlyキーワードは、メンバー変数に定数を宣言するために使用されますが、実行時に値を計算できます。これは、const修飾子で宣言された定数とは異なります。修飾子は、コンパイル時に値を設定する必要があります。を使用readonlyして、宣言、またはフィールドがメンバーであるオブジェクトのコンストラクターでフィールドの値を設定できます。

定数を参照する外部DLLを再コンパイルする必要がない場合にも使用します(コンパイル時に置換されるため)。


193

読み取り専用フィールドを使用することによるパフォーマンスの向上はないと思います。これは、オブジェクトが完全に構築されると、そのフィールドが新しい値を指すことができないことを確認するためのチェックです。

ただし、「読み取り専用」は、CLRによって実行時に適用されるため、他のタイプの読み取り専用セマンティクスとは大きく異なります。readonlyキーワードは、CLRによって検証可能な.initonlyにコンパイルされます。

このキーワードの本当の利点は、不変のデータ構造を生成することです。定義による不変のデータ構造は、いったん構築すると変更できません。これにより、実行時の構造の動作を非常に簡単に推論できます。たとえば、不変構造をコードの別のランダムな部分に渡す危険はありません。彼らはこれを変更することはできませんので、その構造に対して確実にプログラムできます。

ここでは不変性のメリットの1程度の良いエントリは次のとおりです。スレッディング


6
これを読んだ場合、stackoverflow.com / questions / 9860595 /…読み取り専用メンバーは変更でき、.netによる一貫性のない動作のように見えます
Akash Kava

上記のスレッディングに関する投稿へのリンクを更新していただけますか?壊れている。
Akshay Khot

69

を使用しても明らかなパフォーマンス上の利点はありません。readonly少なくとも、どこかで言及したことのないものはありません。それは、あなたが提案したとおりに行うためであり、初期化された後の変更を防ぐためです。

したがって、より堅牢で読みやすいコードを作成するのに役立つという利点があります。このようなことの真のメリットは、チームで作業しているときやメンテナンスのためにあるときに得られます。何かを宣言することreadonlyは、コード内でのその変数の使用法についてのコントラクトを置くことに似ています。internalまたはのような他のキーワードと同じ方法でドキュメントを追加すると考えてprivateください。「この変数は初期化後に変更してはなりません」と言っているだけでなく、強制しています。

したがって、クラスを作成し、一部のメンバー変数readonlyを設計でマークすると、自分または他のチームメンバーが後でクラスを拡張または変更するときに間違いを犯すのを防ぐことができます。私の意見では、それは価値のあるメリットです(コメントでdoofledorferが言及しているように、余分な言語の複雑さを少し犠牲にしてください)。


そしてotohは言語を単純化します。ただし、福利厚生声明を否定するわけではありません。
dkretz 2008年

3
私は同意しますが、実際の利点は、複数の人がコードに取り組んでいるときに得られると思います。これは、コード内に小さな設計ステートメント、つまりその使用に関する契約があるようなものです。私はおそらくそれを答えに入れるべきだと思います。
Xiaofu

7
この回答とディスカッションは、私の意見では実際には最良の回答です。+ 1
ジェフマーティン

@Xiaofu:あなたは私に、この世界の誰もが最も愚かな心を理解できるとは限らないという、読み取り専用のハハハの美しい説明の考えを一定にさせました
学習者

つまり、この値はいつでも変更されるべきではないということをコード内で維持しています。
アンデス

51

非常に実用的に言えば、

dll Aでconstを使用し、dll Bがそのconstを参照している場合、そのconstの値はdll Bにコンパイルされます。dllAをそのconstの新しい値で再デプロイしても、dll Bは元の値を使用します。

DLL Aで読み取り専用を使用し、DLL Bがその読み取り専用を参照する場合、その読み取り専用は実行時に常に検索されます。つまり、その読み取り専用の新しい値でdll Aを再デプロイすると、dll Bはその新しい値を使用します。


4
これは違いを理解するための良い実用的な例です。ありがとう。
Shyju 2016年

一方、ではconstパフォーマンスが向上する可能性がありますreadonly。これがコードによるもう少し深い説明です:dotnetperls.com/readonly
Dio Phung

2
この答えには、実用的な最大の用語が欠けていると思いreadonlyます。実行時に計算された値をフィールドに格納する機能です。IDを変更せずにコンパイル時に他のアセンブリへの参照などの非値のものをベイクできないためnew object();、a constをa に格納することはできません。
binki

14

コンパイラーがreadonlyキーワードの存在に基づいてパフォーマンスを最適化できる可能性があります。

これは、読み取り専用フィールドもstaticとしてマークされている場合にのみ適用されます。その場合、JITコンパイラーは、この静的フィールドは決して変更されないと想定できます。JITコンパイラは、クラスのメソッドをコンパイルするときにこれを考慮に入れることができます。

典型的な例:クラスには、コンストラクターで初期化される静的な読み取り専用のIsDebugLoggingEnabledフィールドが含まれている可能性があります(構成ファイルに基づくなど)。実際のメソッドがJITコンパイルされると、デバッグログが有効になっていない場合、コンパイラはコードのすべての部分を省略できます。

この最適化が現在のバージョンのJITコンパイラに実際に実装されているかどうかは確認していません。そのため、これは単なる推測にすぎません。


これのソースはありますか?
セダットカパノグル2016

4
現在のJITコンパイラは実際にこれを実装しており、CLR 3.5以降に実装されています。github.com/dotnet/coreclr/issues/1079
mirhagk

読み取り専用フィールドは読み取り専用ではなく読み取り/書き込みであるという単純な理由により、読み取り専用フィールドでは最適化を実行できません。これらは、ほとんどのコンパイラーが尊重するコンパイラーのヒントにすぎず、読み取り専用フィールドの値はリフレクションによって簡単に上書きできます(ただし、部分的に信頼されたコードではありません)。
クリストフ

9

readonlyは値自体にのみ適用されるため、参照タイプを使用している場合、readonlyは参照が変更されるのを防ぐだけであることに注意してください。インスタンスの状態は読み取り専用で保護されていません。


4

params readonlyを使用して、コンストラクターの外部でフィールドを設定するための回避策があることを忘れないでくださいout

少し乱雑ですが:

private readonly int _someNumber;
private readonly string _someText;

public MyClass(int someNumber) : this(data, null)
{ }

public MyClass(int someNumber, string someText)
{
    Initialise(out _someNumber, someNumber, out _someText, someText);
}

private void Initialise(out int _someNumber, int someNumber, out string _someText, string someText)
{
    //some logic
}

ここでさらに議論する:http : //www.adamjamesnaylor.com/2013/01/23/Setting-Readonly-Fields-From-Chained-Constructors.aspx


4
フィールドはまだコンストラクタで割り当てられています。「回避」する必要はありません。それは問題ではない値を分解複合型から、単一の式からのもの、またはの参照セマンティクスによる呼び出しによって割り当てられた場合out...
user2864740

これは質問に答えようとさえしません。
シェリダン

2

驚いたことに、Jon Skeetが彼のNoda Timeライブラリをテストしているときに見つけたように、readonlyは実際にはコードが遅くなる可能性があります。この場合、20秒で実行されたテストは、読み取り専用を削除してから4秒しかかかりませんでした。

https://codeblog.jonskeet.uk/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/


3
フィールドがreadonly structC#7.2のタイプである場合、フィールドを非読み取り専用にする利点はなくなります。
Jon Skeet

1

プログラム全体で同じままである必要がある事前定義または事前計算値がある場合は、定数を使用する必要がありますが、実行時に提供する必要があるがプログラム全体で同じままである必要がある値がある場合は、uを使用する必要があります読み取り専用。たとえば、プログラムの開始時間を割り当てる必要がある場合、またはオブジェクトの初期化時にユーザー指定の値を保存する必要があり、それ以上の変更を制限する必要がある場合は、読み取り専用を使用する必要があります。


1

この質問に答えるために基本的な側面を追加します:

プロパティは、set演算子を省略することで読み取り専用として表現できます。したがって、ほとんどの場合readonly、プロパティにキーワードを追加する必要はありません。

public int Foo { get; }  // a readonly property

それとは対照的にreadonly、同様の効果を達成するには、フィールドにキーワードが必要です。

public readonly int Foo; // a readonly field

したがって、フィールドをマークすることの利点の1つは、演算子なしでreadonlyプロパティと同様の書き込み保護レベルを達成できることsetです。何らかの理由でフィールドをプロパティに変更する必要がない場合です。


2つの動作に違いはありますか?
petrosmm

0

プライベートな読み取り専用配列には注意してください。これらがオブジェクトとしてクライアントを公開している場合(私が行ったように、COM相互運用機能に対してこれを行う可能性があります)、クライアントは配列値を操作できます。配列をオブジェクトとして返す場合は、Clone()メソッドを使用します。


20
番号; ReadOnlyCollection<T>配列の代わりに公開します。
SLaks

これは質問に対する回答を提供しないため、回答ではなくコメントにする必要があります...
Peter

おかしなことに、先週の別の投稿でコメントをするのではなく、この種のことを答えにするように言われました。
カイルバラン

2013以降、を使用できますImmutableArray<T>。これにより、インターフェイスへのボックス化(IReadOnlyList<T>)またはクラスでのラッピング()が回避されますReadOnlyCollection。:それは、ネイティブ配列に匹敵する性能を持っているblogs.msdn.microsoft.com/dotnet/2013/06/24/...
チャールズ・テイラー

0

高価なDependencyPropertiesが不要になるため、WPFにはパフォーマンス上の利点があります。これはコレクションで特に役立ちます


0

読み取り専用マーキングの使用のもう1つの興味深い部分は、フィールドをシングルトンでの初期化から保護することです。

たとえばcsharpindepthからのコードでは:

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
}

readonlyは、フィールドシングルトンが2回初期化されるのを防ぐ役割を果たします。別の詳細では、前述のシナリオでは、constはコンパイル時に強制的に作成されるため、constを使用できませんが、シングルトンは実行時に作成されます。


0

readonly宣言時に初期化するか、コンストラクタからのみその値を取得できます。constそれとは異なり、初期化と宣言を同時に行う必要があります。 readonly すべてのもの const に加えて、コンストラクタの初期化があります

コード https://repl.it/HvRU/1

using System;

class MainClass {
    public static void Main (string[] args) {

        Console.WriteLine(new Test().c);
        Console.WriteLine(new Test("Constructor").c);
        Console.WriteLine(new Test().ChangeC()); //Error A readonly field 
        // `MainClass.Test.c' cannot be assigned to (except in a constructor or a 
        // variable initializer)
    }


    public class Test {
        public readonly string c = "Hello World";
        public Test() {

        }

        public Test(string val) {
          c = val;
        }

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