メンバー変数を読み取り専用として宣言する利点は何ですか? クラスのライフサイクル中に誰かがその値を変更するのを防ぐだけですか、それともこのキーワードを使用すると速度や効率が向上しますか?
14 に答える
読み取り専用フィールドを使用してもパフォーマンスが向上するとは思いません。オブジェクトが完全に構築されると、そのフィールドが新しい値を指すことができないことを確認するための単純なチェックです。
ただし、「読み取り専用」は、CLR によって実行時に適用されるため、他の種類の読み取り専用セマンティクスとは大きく異なります。readonly キーワードは、CLR によって検証可能な .initonly にコンパイルされます。
このキーワードの本当の利点は、不変のデータ構造を生成できることです。定義による不変データ構造は、一度構築されると変更できません。これにより、実行時の構造体の動作を簡単に判断できます。たとえば、不変の構造体をコードの別のランダムな部分に渡す危険はありません。彼らはそれを変更することはできないので、その構造に対して確実にプログラムすることができます。
Robert Pickering は、不変性の利点に関する優れたブログ記事を書いています。この投稿は、こちらまたはarchive.org のバックアップにあります。
このreadonly
キーワードは、メンバー変数を定数として宣言するために使用されますが、実行時に値を計算できるようにします。const
これは、コンパイル時に値を設定する必要がある修飾子で宣言された定数とは異なります。を使用readonly
すると、宣言内、またはフィールドがメンバーであるオブジェクトのコンストラクター内で、フィールドの値を設定できます。
また、定数を参照する外部 DLL を再コンパイルする必要がない場合にも使用します (コンパイル時に置き換えられるため)。
を使用することによる明らかなパフォーマンス上の利点はありませんreadonly
。少なくとも、これまでどこでも言及されたことはありません。初期化された後の変更を防ぐために、あなたが提案したとおりに行うためだけです。
したがって、より堅牢で読みやすいコードを作成するのに役立つという点で有益です。このようなことの真の利点は、チームで作業している場合やメンテナンスのためにもたらされます。何かを as として宣言することreadonly
は、コード内でその変数の使用法を規定することに似ています。internal
またはのような他のキーワードと同じ方法でドキュメントを追加すると考えてprivate
ください。「この変数は初期化後に変更しないでください」と言っており、さらにそれを強制しています。
したがって、クラスを作成し、readonly
設計によっていくつかのメンバー変数をマークすると、後でクラスを拡張または変更するときに、自分自身または他のチーム メンバーが間違いを犯すのを防ぐことができます。私の意見では、それは持つ価値のある利点です (コメントで doofledorfer が言及しているように、言語の複雑さが多少犠牲になります)。
非常に実用的な言葉で言えば:
dll A で const を使用し、dll B がその const を参照する場合、その const の値は dll B にコンパイルされます。その const の新しい値で dll A を再デプロイすると、dll B は元の値を引き続き使用します。
読み取り専用の dll A および dll B 参照で読み取り専用を使用すると、その読み取り専用は実行時に常に検索されます。これは、その読み取り専用の新しい値で dll A を再デプロイすると、dll B がその新しい値を使用することを意味します。
readonly キーワードの存在に基づいて、コンパイラがパフォーマンスの最適化を行うことができる潜在的なケースがあります。
これは、読み取り専用フィールドがstaticとしてマークされている場合にのみ適用されます。その場合、JIT コンパイラーは、この静的フィールドが決して変更されないと想定できます。JIT コンパイラーは、クラスのメソッドをコンパイルするときにこれを考慮に入れることができます。
典型的な例: クラスは、コンストラクターで初期化される静的な読み取り専用のIsDebugLoggingEnabledフィールドを持つことができます (たとえば、構成ファイルに基づいて)。実際のメソッドが JIT コンパイルされると、デバッグ ログが有効になっていない場合、コンパイラはコード全体を省略できます。
この最適化が現在のバージョンの JIT コンパイラに実際に実装されているかどうかは確認していないため、これは推測にすぎません。
readonly は値自体にのみ適用されることに注意してください。したがって、参照型を使用している場合、 readonly は参照を変更から保護するだけです。インスタンスの状態は、読み取り専用によって保護されていません。
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
プログラム全体で同じままにしておく必要がある事前定義または事前計算された値がある場合は、定数を使用する必要がありますが、実行時に提供する必要がある値がある場合は、一度割り当てられると、プログラム全体で同じままにする必要があります。読み取り専用。たとえば、プログラムの開始時刻を割り当てる必要がある場合、またはオブジェクトの初期化時にユーザーが提供した値を保存する必要があり、それ以降の変更を制限する必要がある場合は、読み取り専用を使用する必要があります。
readonly マーキングのもう 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 は、フィールド Singleton が 2 回初期化されるのを防ぐという小さな役割を果たします。別の詳細は、上記のシナリオでは、const はコンパイル時に作成を強制するため const を使用できないが、シングルトンは実行時に作成することです。
プライベートな読み取り専用配列には注意してください。これらがオブジェクトとしてクライアントに公開されている場合 (私が行ったように、COM 相互運用のためにこれを行うことができます)、クライアントは配列値を操作できます。配列をオブジェクトとして返す場合は、Clone() メソッドを使用します。
WPFでは、高価なDependencyPropertiesが不要になるため、パフォーマンスが向上する可能性があります。これは、コレクションで特に役立ちます