12

まず、紹介:

このコード:

class C
{
    int i = 5;
    byte[] s = new byte[i];
}

次のエラーでコンパイルに失敗します。

フィールド初期化子は、非静的フィールド、メソッド、またはプロパティ `Ci' を参照できません

Resharper も同様のことを言っています:静的コンテキストでは非静的フィールドにアクセスできません。i

これは、C# 仕様の内容と一致しています。つまり、フィールド初期化子は、現在作成中のインスタンス ( this) にアクセスすることはできず、拡張により、インスタンス フィールドのいずれにもアクセスできません。

インスタンス フィールドの変数初期化子は、作成中のインスタンスを参照できません。したがって、変数初期化子で this を参照するとコンパイル時エラーになります。これは、変数初期化子が単純名を介して任意のインスタンス メンバーを参照するとコンパイル時エラーになるためです。

ただし、これは Java では問題なく機能します。

class C {
    int i = 5;
    byte s[] = new byte[i]; //no errors here
}

まだ私と一緒に?わかりました、ここで質問です。えっと、質問です。

これが C# で有効になる仮想の世界では、私は疑問に思っています:それは可能でしょうか? もしそうなら、テーブルに追加される長所と短所は何ですか? また、実際に Java でサポートされているため、Java にも同じ長所/短所が ありますか? それとも、2 つの言語で型初期化子が機能する方法に根本的な違いはありますか?

4

5 に答える 5

13

つまり、コンストラクター本体が実行される前にレシーバーにアクセスできるということは、バグのあるプログラムを作成しやすくする、わずかな利点の機能です。そのため、C# 言語の設計者はそれを完全に無効にしました。レシーバーを使用する必要がある場合は、そのロジックをコンストラクター本体に入れます。

この機能が Java で合法である理由については、Java 設計者に尋ねる必要があります。

于 2013-06-27T13:38:50.910 に答える
5

C# では、フィールド初期化子は開発者にとって便利なセマンティクスにすぎません。コンパイラは、すべてのフィールド初期化子をコンストラクタABOVEの本体に移動し、そこで基本コンストラクタへの呼び出しが行われます。したがって、フィールドは祖先チェーンを上に向かって初期化され、クラスはベースから下に向かって初期化されます。

静的参照は、何よりも先に初期化されるため問題ありません。

于 2013-06-27T11:54:39.897 に答える
2

これは決して信頼できる答えではありませんが、知識に基づいた推測をさせてください。

根本的な違いがあり他の質問への回答はこの違いに関連していると思います。
特に継承のコンテキストでは、型の初期化の順序にあります。

では、インスタンスの初期化はどのように機能するのでしょうか?

C# の場合:

  • すべてのインスタンス フィールド初期化子が最初に実行され、ほとんどの派生クラスから基本クラスへの継承チェーンが「上に」実行されます。

  • 次に、ctor がベースから派生物まで、チェーンを「下って」実行されます。

ctor が相互に呼び出したり、(明示的に) 基本クラスの ctor を呼び出したりする可能性は、状況を変えないので、省略します。

基本的に何が起こるかというと、これは最も派生したものから始めて、チェーン内の各 chass に対して実行されます。

Derived.initialize(){
    derivedInstance.field1 = field1Initializer();
    [...]
    Base.Initialize();
    Derived.Ctor();
}

簡単な例でこれを示します。

void Main()
{
    new C();
}
class C: B {
    public int c = GetInt("C.c");
    public C(){
        WriteLine("C.ctor");
    }
}
class B {
    public int b = GetInt("B.b");
    public static int GetInt(string _var){
        WriteLine(_var);
        return 6;
    }
    public B(){
        WriteLine("B.ctor");
    }
    public static void WriteLine(string s){
        Console.WriteLine(s);
    }
}

出力:

C.c
B.b
B.ctor
C.ctor

つまり、フィールド初期化子のフィールドへのアクセスが有効である場合、次のような惨事が発生する可能性があります

class C: B {
    int c = b; //b is a field inherited from the base class, and NOT YET INITIALIZED!
    [...]
}

Java の場合:

型の初期化に関する長くて興味深い記事はこちらです。要約する:

インスタンスフィールド初期化子の概念に加えて、(オプションの)インスタンス初期化子の概念があるため、もう少し複雑ですが、その要点は次のとおりです。

すべてが継承チェーンを実行します。

  • 基本クラスのインスタンス初期化子が実行されます
  • 基本クラス実行のフィールド初期化子
  • 基本クラス実行の ctor(s)

  • 継承チェーンの次のクラスに対して上記の手順を繰り返します。

  • 最も派生したクラスに到達するまで、前の手順を繰り返します。

証拠は次のとおりです: (またはオンラインで自分で実行します)

class Main
{
    public static void main (String[] args) throws java.lang.Exception
    {
      new C();
    }
}

class C extends B {
    {
        WriteLine("init C");
    }
    int c = GetInt("C.c");

    public C(){
            WriteLine("C.ctor");
    }

}

class B {
    {
        WriteLine("init B");
    }
    int b = GetInt("B.b");

    public static int GetInt(String _var){
            WriteLine(_var);
            return 6;
    }
    public B(){
            WriteLine("B.ctor");
    }
    public static void WriteLine(String s){
            System.out.println(s);
    }

}

出力:

init B
B.b
B.ctor
init C
C.c
C.ctor

これが意味することは、フィールド初期化子が実行されるまでに、継承されたすべてのフィールドが (基本クラスの初期化子または ctor によって) 既に初期化されているため、この動作を許可するのに十分安全です。

class C: B {
    int c = b; //b is inherited from the base class, and it's already initialized!
    [...]
}

Java では、C# と同様に、フィールド初期化子は宣言順に実行されます。
Java コンパイラは、フィールド初期化子が out-of-order* と呼ばれていないことを確認する努力さえします。

class C {
    int a = b; //compiler error: illegal forward reference
    int b = 5;
}

* 余談ですが、イニシャライザがインスタンス メソッドを呼び出すと、順不同でフィールドにアクセスできます。

class C {
    public int a = useB(); //after initializer completes, a == 0
    int b = 5;
    int useB(){
        return b;  //use b regardless if it was initialized or not.
    }
}
于 2013-06-27T14:11:08.667 に答える
-1

これは少し答えになりませんが、クラスの本体にあるものはすべてシーケンスに依存しないと考えるのが好きです。これは、特定の方法で評価する必要がある順次コードであってはなりません。これは、クラスのデフォルト状態にすぎません。そのようなコードを使用する場合、s の前に i が評価されることを期待しています。

とにかく、とにかく ia const を作ることができます(そうあるべきです)。

于 2013-06-27T12:03:05.273 に答える
-1

これは、フィールド初期化子がコンパイラによってコンストラクターに移動されるため (静的でない場合)、次のようにコンストラクターで明示的にする必要があるためです。

class C 
{
    int i = 5;
    byte[] s;

    public C()
    {
        s = new byte[i];
    }
}
于 2013-06-27T12:00:10.090 に答える