web-dev-qa-db-ja.com

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

メンバー変数を読み取り専用として宣言することの利点は何ですか?クラスのライフサイクル中に誰かが変更されるのを防ぐだけですか、それともこのキーワードによるコンパイラ速度の改善がありますか

259
leora

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

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

148
Bill the Lizard

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

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

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

不変性の利点の1つに関する優れたエントリを次に示します。 Threading

179
JaredPar

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

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

したがって、クラスを作成し、いくつかのメンバー変数readonlyを設計によりマークすると、後で自分自身または別のチームメンバーがクラスを拡張または変更するときにミスを犯すことを防ぎます。私の意見では、それは価値があります(doofledorferがコメントで言及しているように、言語の複雑さを少し犠牲にします)。

61
Xiaofu

非常に実用的な言葉で言えば:

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

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

44
Daniel Auger

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

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

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

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

12

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

7
Brian Rasmussen

readonly paramsを使用して、コンストラクターの外部で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

6
Adam Naylor

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

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

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

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

public readonly int Foo; // a readonly field

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

2
code14214

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

2
waqar ahmed

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

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

1
Neil

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

codehttps://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 ;
        }
    }
}
1
Mina Gabriel

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

1
Michael

読み取り専用マーキングの使用のもう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回初期化されるのを防ぐ小さな役割を果たします。もう1つの詳細は、前述のシナリオではconstを使用できないことです。constはコンパイル時に作成を強制しますが、シングルトンは実行時に作成を行います。

0
Yuriy Zaletskyy

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

0
Shane