字段初始化程序访问'this'重新加载

InB*_*een 8 c#

这个问题是Cristi Diaconescu关于this在C#中访问字段初始化程序的非法性的扩展.

这在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.
}
Run Code Online (Sandbox Code Playgroud)

好的,所以合理解释为什么这是非法的,其中包括Eric Lippert:

简而言之,在构造函数体运行之前访问接收器的能力是边缘效益的特征,这使得编写错误程序更容易.因此,C#语言设计者完全禁用它.如果需要使用接收器,则将该逻辑放在构造函数体中.

此外,C#规范非常简单(直到某一点):

实例字段的变量初始值设定项无法引用正在创建的实例.因此,在变量初始化程序中引用它是一个编译时错误,因为变量初始化程序通过简单名称引用任何实例成员是编译时错误.

所以我的问题是:" 通过一个简单的名字 "是什么意思?

是否存在合法的替代机制?我确信规范中几乎每个单词都有一个非常具体的原因,那么通过简单的名称将这个特定代码的非法性限制为引用的原因是什么?

编辑:我没有把我的问题措辞得太好.我不是要求" 简单名称 " 的定义,我问的是将非法性限制在特定情况的原因.如果以任何方式引用任何实例成员始终是非法的,那么为什么要如此狭窄地指定呢?如果不是,那么什么机制合法呢?

小智 5

在一般情况下,不可能确定表达式是否指向正在构造的对象,因此禁止它并要求编译器诊断它将需要不可能的.考虑

partial class A {
  public static A Instance = CreateInstance();
  public int a = 3;
  public int b = Instance.a;
}
Run Code Online (Sandbox Code Playgroud)

这是可能的,据我所知完全有效,即使它是一个可怕的想法,创建一个对象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);
  }
}
Run Code Online (Sandbox Code Playgroud)

您只能在编译时获得可检测的编译器错误.