26

C# アプリで作業しているときに、いくつかの場所で静的初期化子が次のように相互に依存していることに気付きました。

static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };

うまくいった特別なことは何もしません。それはただの運ですか?C# にはこれを解決するルールがありますか?

編集: (re: パノス) ファイルの字句順は王様のようですか? ファイル間ではどうですか?

見てみると、次のような循環依存関係を試しました。

static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { a[0] };

そして、プログラムは同じように実行されませんでした (テスト スーツは全面的に失敗し、私はそれ以上調べませんでした)。

4

4 に答える 4

22

ルールについては、C# 仕様のセクション 10.4 を参照してください。

クラスが初期化されると、そのクラスのすべての静的フィールドが最初にデフォルト値に初期化され、次に静的フィールド初期化子がテキスト順に実行されます。同様に、クラスのインスタンスが作成されると、そのインスタンスのすべてのインスタンス フィールドが最初にデフォルト値に初期化され、次にインスタンス フィールド初期化子がテキスト順に実行されます。変数初期化子を持つ静的フィールドがデフォルト値の状態で観察される可能性があります。ただし、スタイルの問題として、これは強くお勧めできません。

つまり、あなたの例では、「b」はデフォルト状態 (null) に初期化されているため、「a」の初期化子での参照は有効ですが、NullReferenceException が発生します。

これらの規則は、Java の規則とは異なります ( 前方参照に関する Java の規則については、より制限的なJLS のセクション 8.3.2.3 を参照してください)。

于 2008-10-08T23:59:33.113 に答える
15

行の順番にもよるようです。このコードは機能します:

static private List<int> a = new List<int>() { 1 };
static private List<int> b = new List<int>() { a[0] };

このコードは機能しませんが (スローされますNullReferenceException)

static private List<int> a = new List<int>() { b[0] };
static private List<int> b = new List<int>() { 1 };

したがって、明らかに循環依存のル​​ールは存在しません。ただし、コンパイラが文句を言わないのは独特です...


編集 - 「ファイル全体」で何が起こっていますか? これら 2 つのクラスを宣言すると、次のようになります。

public class A {
    public static List<int> a = new List<int>() { B.b[0] };
}
public class B {
    public static List<int> b = new List<int>() { A.a[0] };
}

そして、次のコードでそれらにアクセスしてみてください:

try { Console.WriteLine(B.b); } catch (Exception e) { Console.WriteLine(e.InnerException.Message.); }
try { Console.WriteLine(A.a); } catch (Exception e) { Console.WriteLine(e.InnerException.Message); }
try { Console.WriteLine(B.b); } catch (Exception e) { Console.WriteLine(e.InnerException.Message); }

次の出力が得られます。

The type initializer for 'A' threw an exception.
Object reference not set to an instance of an object.
The type initializer for 'A' threw an exception.

そのため、の初期化Bにより、静的コンストラクターで例外が発生し、デフォルト値 (null) のAlefts フィールドが発生します。ですaので、正常に初期化することもできません。anullb

周期的な依存関係がなければ、すべてうまくいきます。


編集: コメントを読んでいない場合に備えて、Jon Skeetは非常に興味深い読み物を提供しています:静的コンストラクターと型初期化子の違い

于 2008-10-08T23:54:31.127 に答える
2

個人的には、明確ではないので静的イニシャライザを取り除き、静的コンストラクタを追加してこれらの変数を初期化します。

static private List<int> a;
static private List<int> b;

static SomeClass()
{
    a = new List<int>() { 0 };
    b = new List<int>() { a[0] };
}

そうすれば、何が起こっているのかを推測する必要がなくなり、意図が明確になります。

于 2008-10-08T23:59:13.130 に答える
0

はい、あなたは幸運でした。C# は、クラスに表示される順序でコードを実行するように見えます。

static private List<int> a = new List<int>() { 0 };
static private List<int> b = new List<int>() { a[0] };

動作しますが...

static private List<int> b = new List<int>() { a[0] };
static private List<int> a = new List<int>() { 0 };

失敗します。

すべての依存関係を 1 か所に配置することをお勧めします。静的コンストラクターはそのための場所です。

static MyClass()
{
  a = new List<int>() { 0 };
  b = new List<int>() { a[0] };
}
于 2008-10-08T23:58:26.847 に答える