web-dev-qa-db-ja.com

C#メンバー変数の初期化。ベストプラクティス?

宣言時にクラスメンバー変数を初期化する方が良いですか

private List<Thing> _things = new List<Thing>();
private int _arb = 99;

またはデフォルトのコンストラクタで?

private List<Thing> _things;
private int _arb;

public TheClass()
{
  _things = new List<Thing>();
  _arb = 99;
}

それは単にスタイルの問題ですか、それともパフォーマンスのトレードオフがありますか?

91
Steve Crane

パフォーマンスの面では、実際の違いはありません。フィールド初期化子は、コンストラクターロジックとして実装されます。唯一の違いは、フィールド初期化子が「base」/「this」コンストラクターの前に発生することです。

コンストラクターアプローチは、自動実装プロパティで使用できます(フィールド初期化子は使用できません)-つまり.

[DefaultValue("")]
public string Foo {get;set;}
public Bar() { // ctor
  Foo = "";
}

それ以外は、フィールド初期化構文を好む傾向があります。私はそれが物事をローカライズすることを見つけます-すなわち.

private readonly List<SomeClass> items = new List<SomeClass>();
public List<SomeClass> Items {get {return items;}}

それが割り当てられている場所を見つけるために狩りに行く必要はありません...

明らかな例外は、複雑なロジックを実行する必要がある場合、またはコンストラクターパラメーターを処理する必要がある場合です。この場合、コンストラクターベースの初期化が必要です。同様に、複数のコンストラクターがある場合は、フィールドが常に同じ方法で設定されることが望ましいでしょう。したがって、次のようなctorが存在する可能性があります。

public Bar() : this("") {}
public Bar(string foo) {Foo = foo;}

編集:サイドコメントとして、上記で、フィールド初期化子を持つ他のフィールド(図示せず)がある場合、それらはbase(...)を呼び出すコンストラクターで直接初期化されることに注意してください-すなわち、public Bar(string foo) ctor。他のコンストラクターは、this(...) ctorによって実行されることを知っているため、フィールド初期化子をnot実行します。

76
Marc Gravell

実際に、実演するフィールド初期化子は便利な速記です。コンパイラーは、実際に初期化コードを、ユーザーが型に定義する各インスタンスコンストラクターの先頭にコピーします。

これには2つの意味があります。1つ目は、各コンストラクターでフィールド初期化コードが複製され、2つ目は、特定の値にフィールドを初期化するためにコンストラクターに含めるコードが実際にフィールドを再割り当てすることです。

そのため、パフォーマンス面では、コンパイルされたコードサイズに関しては、フィールド初期化子をコンストラクターに移動する方が適切です。

一方、パフォーマンスへの影響とコードの「膨張」は通常無視できます。フィールド初期化構文には、コンストラクターのいずれかのフィールドを初期化することを忘れるリスクを減らすという重要な利点があります。

9
Tor Haugen

フィールド初期化子の主な制限の1つは、それらをtry-finallyブロックにラップする方法がないことです。フィールド初期化子で例外がスローされると、以前の初期化子で割り当てられたリソースはすべて破棄されます。それを防ぐ方法はありません。厄介な場合は、保護されたベースコンストラクターが参照によってIDisposableを受け入れ、それを最初の操作としてそれ自体を指すようにすることで、構築におけるその他のエラーに対処できます。その後、例外が発生した場合に部分的に作成されたオブジェクトでDisposeを呼び出すファクトリーメソッドを除いて、コンストラクターの呼び出しを回避できます。この保護により、新しいオブジェクトへの参照を「密輸」した後にメインクラスのコンストラクターが失敗した場合、派生クラスのイニシャライザーで作成されたIDisposablesをクリーンアップできます。残念ながら、フィールド初期化子が失敗した場合、そのような保護を提供する方法はありません。

4
supercat

フィールド初期化子を使用するか、Init()関数を作成します。これらのものをコンストラクターに入れることの問題は、2番目のコンストラクターを追加する必要がある場合、コードのコピー/貼り付けが行われる(または見落として初期化されていない変数が作成される)ことです。

宣言された場所を初期化します。または、コンストラクターでInit()関数を呼び出すようにします。

2
GeekyMonkey

インスタンス変数については、主にスタイルの問題です(コンストラクターを使用することを好みます)。静的変数の場合、インライン初期化には パフォーマンス上の利点 があります(もちろん常に可能とは限りません)。

1
Kent Boogaart

上記に追加した点-実装を持つクラスを実装するときは、常にコンストラクターがあります。宣言しない場合、デフォルトのインストラクターはコンパイラーによって推測されます[public Foo(){}];引数を取らないコンストラクタ。

多くの場合、私は両方のアプローチを提供したいです。コンストラクターを使用したい場合はコンストラクターを許可し、クラス/タイプの単純化された実装またはデフォルトの実装を使用したい場合はフィールド初期化子を許可します。これにより、コードに柔軟性が追加されます。誰でもデフォルトのフィールド初期化子を選択できれば使用できることに注意してください...複数のコンストラクタを提供する場合は、手動で宣言してください-public Foo(){}

0
Qubits

本当にあなた次第です。

0
Nico