9

この質問は、C# でアクセスするフィールド初期化子の違法性に関するCristi Diaconescu の拡張です。this

これは C# では違法です。

class C
{
    int i = 5;
    double[] dd = new double[i]; //Compiler error: A field initializer cannot reference the non-static field, method, or property.
}

わかりました、なぜこれが違法であるかについての合理的な説明は、とりわけ、Eric Lippertによって与えられます:

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

また、C# の仕様はかなり単純です (ある程度までは)。

インスタンス フィールドの変数初期化子は、作成中のインスタンスを参照できません。したがって、変数初期化子が simple-name を介して任意のインスタンス メンバーを参照するのはコンパイル時エラーであるため、変数初期化子で this を参照するのはコンパイル時エラーです。

だから私の質問は、「単純な名前を介して」とはどういう意味ですか?

これが合法になる代替メカニズムはありますか?仕様のほぼすべての単語が非常に特定の理由で存在することは確かですが、この特定のコードの違法性を単純な名前による参照に制限する理由は何ですか?

編集:私は自分の質問をうまく言い表していません。「単純名」の定義を求めているのではなく、その特定のシナリオに違法性を限定する理由について尋ねています。どのような方法でもインスタンス メンバーを参照することが常に違法である場合、なぜそれをそれほど狭く指定するのでしょうか? そうでない場合、どのメカニズムが合法でしょうか?

4

4 に答える 4

6

一般に、式が構築中のオブジェクトを参照しているかどうかを判断することは不可能であるため、それを禁止し、コンパイラーに診断を求めることは、不可能なことを要求します。検討

partial class A {
  public static A Instance = CreateInstance();
  public int a = 3;
  public int b = Instance.a;
}

でオブジェクトを作成し、それFormatterServices.GetUninitializedObject(typeof(A))に設定A.Instanceして、コンストラクターを呼び出すことは可能です。がb初期化されると、オブジェクトは自身のaメンバーを読み取ります。

partial class A {
  public static A CreateInstance() {
    Instance = (A)FormatterServices.GetUninitializedObject(typeof(A));
    var constructor = typeof(A).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
    var helperMethod = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(A) }, typeof(A).Module, true);
    var ilGenerator = helperMethod.GetILGenerator();
    ilGenerator.Emit(OpCodes.Ldarg_0);
    ilGenerator.Emit(OpCodes.Call, constructor);
    ilGenerator.Emit(OpCodes.Ret);
    var constructorInvoker = (Action<A>)helperMethod.CreateDelegate(typeof(Action<A>));
    constructorInvoker(Instance);
    return Instance;
  }
}

static class Program {
  static void Main() {
    Console.WriteLine("A.Instance = (a={0}, b={1})", A.Instance.a, A.Instance.b);
  }
}

コンパイル時に検出可能なコンパイラ エラーのみを取得できます。

于 2013-06-28T17:11:51.167 に答える
1

アンマネージ コードが登場すると、いつでもめちゃくちゃなことをすることができます。このことを考慮:

public class A
{
    public int n = 42;
    public int k = B.Foo();

    public A()
    {

    }
}

public class B
{
    public static unsafe int Foo()
    {
        //get a pointer to the newly created instance of A 
        //through some trickery.  
        //Possibly put some distinctive field value in `A` to make it easier to find

        int i = 0;
        int* p = &i;
        //get p to point to n in the new instance of `A`

        return *p;
    }
}

私はこれを実際に実装しようと少し時間を費やしましたが(キックのために)、少し後にあきらめました。つまり、ヒープへのポインターを取得してから、インスタンスとして認識できるものを探し始め、そこから値をA取得できnます。難しいでしょうが、可能です。

于 2013-06-28T16:56:53.953 に答える
-1

最後の文を読み違えているだけだと思います。仕様は、インスタンス フィールド初期化子が作成中のインスタンスを参照できないことをきっぱりと述べています。次に、単に例を引用しています。this単純な名前のアクセスでは暗黙的に が使用されるため、同じ理由で「単純な名前」を使用することはできませんthis。仕様はケースを狭めていません。違法な特定の構造を単に呼び出しているだけです。別のものはbase、基本クラスから保護されたフィールドにアクセスするために使用されます。

于 2013-06-28T18:23:12.747 に答える