13

好奇心旺盛ですが、問題を解決しようとしているわけではありません。

ローカル変数のみを割り当てる必要があるのはなぜですか?

次の例では:

class Program
{
    static int a;
    static int b { get; set; }
    static void Main(string[] args)
    {
        int c;
        System.Console.WriteLine(a);
        System.Console.WriteLine(b);
        System.Console.WriteLine(c);
    }
}

なぜab警告だけが表示されc、エラーが表示されるのですか?

さらに、 Value Typeのデフォルト値を使用して次のコードを記述できないのはなぜですか?

        bool MyCondition = true;
        int c;
        if (MyCondition)
            c = 10;

メモリ管理と関係がありますか?

4

3 に答える 3

26

ティムはあなたの最初の質問に良い答えを与えますが、私はもう少し詳細を追加することができます。

最初の質問は、基本的に「ローカルは確実に割り当てられる必要があるので、フィールドに同じ制限を加えてみませんか?」です。ティムは、ジョンがそうすることは実際にはかなり難しいと指摘していると指摘します。ローカルの場合、ローカルが最初に読み取られたときと最初に書き込まれたときは、ほとんどの場合非常に明確です。明確でない場合、コンパイラーは合理的な控えめな推測を行うことができます。ただし、フィールドでは、最初の読み取りと最初の書き込みがいつ発生するかを知るには、どのメソッドがどの順序で呼び出されるかについて、あらゆる種類のことを知る必要があります。

つまり、ローカル分析にはローカル分析が必要です。分析は、宣言を含むブロックを通過する必要はありません。フィールド分析にはグローバル分析が必要です。それは大変な作業です。フィールドがデフォルト値に初期化されていると言う方が簡単です。

(そうは言っても、もちろんこのグローバル分析を行うことは可能です。私の新しい仕事には、この種の分析を正確に行うことが含まれる可能性があります。)

2番目の質問は、基本的に「では、デフォルト値の自動割り当てがフィールドに十分である場合、なぜローカルに十分ではないのですか?」です。答えは、「ローカル変数の割り当てに失敗し、誤ってデフォルト値を取得することがバグの一般的な原因であるため」です。C#は、苛立たしいバグにつながるプログラミング手法を阻止するように注意深く設計されており、これはその1つです。

于 2013-01-19T22:05:18.630 に答える
21

他の変数はデフォルト値で初期化されるためです。

Jon Skeet は、この問題に関して興味深い言葉をいくつか見つけました。

ローカル変数の場合、コンパイラーはフローの良いアイデアを持っています。変数の「読み取り」と変数の「書き込み」を確認し、(ほとんどの場合) 最初の書き込みが最初の読み取りの前に発生することを証明できます。 .

これは、インスタンス変数には当てはまりません。単純なプロパティを考えてみましょう。誰かがそれを取得する前に設定するかどうかをどうやって知るのでしょうか? したがって、すべてのフィールドがコンストラクターで設定されていることを確認するか、デフォルト値を持つことを許可する必要があります。C# チームは後者の戦略を選択しました。

関連するC# 言語仕様は次のとおりです。

5.3 明確な代入

関数メンバーの実行可能コード内の特定の場所で、コンパイラが特定の静的フロー分析 (§5.3.3) によって、変数が自動的に初期化されているか、変数が自動的に初期化されていることを証明できる場合、変数は確実に割り当てられていると言われます。少なくとも 1 つの割り当てのターゲット。

5.3.1 初期割り当て変数

次のカテゴリの変数は、最初に割り当てられたものとして分類されます。

  • 静的変数。

  • クラス インスタンスのインスタンス変数。

  • 最初に割り当てられた構造体変数のインスタンス変数。

  • 配列要素。

  • 値パラメーター。

  • 参照パラメーター。

  • catch 句または foreach ステートメントで宣言された変数。

5.3.2 最初に割り当てられていない変数

次のカテゴリの変数は、最初は割り当てられていないものとして分類されます。

  • 最初に割り当てられていない構造体変数のインスタンス変数。

  • 構造体インスタンス コンストラクターの this 変数を含む出力パラメーター。

  • ローカル変数 (catch 句または foreach ステートメントで宣言されたものを除く)。

于 2013-01-19T21:42:43.557 に答える
9

CLRは、ローカル変数がデフォルト値に初期化されることを保証します。ただし、この保証には制限があります。不足しているのは、メソッド本体内のスコープブロックを認識する機能です。コンパイラがコードをILに変換すると、これらは消えます。スコープは、CLIに類似点がなく、ILで表現できない言語構造です。

たとえば、VB.NETのような言語ではこれがうまくいかないことがわかります。この不自然な例は、動作を示しています。

Module Module1
    Sub Main()
        For ix = 1 To 3
            Dim s As String
            If ix = 2 Then s = "foo"
            If s Is Nothing Then Console.WriteLine("null") Else Console.WriteLine(s)
        Next
        Console.ReadLine()
    End Sub
End Module

出力:

null
foo
foo

つまり、ローカル変数sは1回だけ初期化され、その後もその値を保持します。もちろん、これにはバグを作成するコツがあります。VB.NETコンパイラ警告を生成し、それを回避するための単純な構文を備えています(新規として)。C ++ / CLIのような管理言語は同じ動作をしますが、診断をまったく出力しません。ただし、C#言語はより強力な保証を提供し、エラーを生成します。

このルールは「明確な割り当て」と呼ばれます。正確なルールについては、C#言語仕様の5.3.3章で詳しく説明されています。

明確な割り当てチェックには制限があります。ローカル変数のスコープは非常に制限されており(メソッド本体に限定されている)、Reflectionを使用してそれらに到達できないため、ローカル変数に対して適切に機能します。クラスのフィールドを処理するのははるかに困難であり、コンパイラが認識できる範囲を超えて到達する必要があるプログラム全体の分析が必要になる場合があります。別のアセンブリのコードのように。これが、C#コンパイラが警告することしかできず、コードを完全に拒否できない理由です。

于 2013-01-19T22:22:17.070 に答える