C# 字段初始值设定项访问';这';重新加载

C# 字段初始值设定项访问';这';重新加载,c#,C#,这个问题是关于字段初始值设定项在C#中访问This的非法性的扩展 这在C#中是非法的: 好的,关于为什么这是非法的合理解释如下: 简而言之,在构造器主体运行之前访问接收器的能力是一个边际优势,这使得编写有缺陷的程序变得更容易。因此,C语言设计者完全禁用了它。如果需要使用接收器,则将该逻辑放入构造函数主体中 此外,C#规范非常简单(在某种程度上): 实例字段的变量初始值设定项不能引用正在创建的实例。因此,在变量初始值设定项中引用它是编译时错误,因为变量初始值设定项通过简单名称引用任何实例成员是编译

这个问题是关于字段初始值设定项在C#中访问
This
的非法性的扩展

这在C#中是非法的:

好的,关于为什么这是非法的合理解释如下:

简而言之,在构造器主体运行之前访问接收器的能力是一个边际优势,这使得编写有缺陷的程序变得更容易。因此,C语言设计者完全禁用了它。如果需要使用接收器,则将该逻辑放入构造函数主体中

此外,C#规范非常简单(在某种程度上):

实例字段的变量初始值设定项不能引用正在创建的实例。因此,在变量初始值设定项中引用它是编译时错误,因为变量初始值设定项通过简单名称引用任何实例成员是编译时错误

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

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

编辑:我的问题措辞不太好。我不是在问“简单名称”的定义,我是在问将非法性限制在特定情况下的原因。如果以任何方式引用任何实例成员都是非法的,那么为什么要如此狭隘地指定它呢?如果不是,那么什么机制才是合法的呢

简单名称由单个标识符组成

我想他们澄清了这一点,因为
this.I
相当于类方法中的
I
,而范围中没有名为
I
的变量。他们已经禁止在实例方法之外使用
this

class C
{
    int i = 5;
    double[] dd = new double[this.i]; 
    //Compiler error: Keyword 'this' is not available in the current context.
}
如果没有这种语言,有些人可能会认为这意味着您可以通过省略关键字
this
来引用实例变量

最好的选择是使用构造函数:

class C
{
    int i = 5;
    double[] dd;
    C()
    {
        dd = new double[i];
    }
}
您也可以这样做:

class C
{
    public int i = 5;
}
class D
{
    double[] dd = new double[new C().i];
}

由于这两个成员在不同的类中,因此它们的初始化顺序是明确的。

当非托管代码发挥作用时,您总是可以做一些乱七八糟的事情。考虑这一点:

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
值。这很难,但却是可能的。

在一般情况下,不可能确定表达式是否引用正在构造的对象,因此禁止它并要求编译器对其进行诊断是不可能的。考虑

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);
  }
}
部分A类{
publicstaticacreateinstance(){
实例=(A)FormatterServices.GetUninitializedObject(typeof(A));
var constructor=typeof(A).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,null,Type.EmptyTypes,null);
var helperMethod=newdynamicmethod(string.Empty,typeof(void),new[]{typeof(A)},typeof(A).Module,true);
var ilGenerator=helperMethod.GetILGenerator();
ilGenerator.Emit(操作码.Ldarg_0);
ilGenerator.Emit(操作码.Call,构造函数);
ilGenerator.Emit(操作码.Ret);
var constructorInvoker=(Action)helperMethod.CreateDelegate(typeof(Action));
constructorInvoker(实例);
返回实例;
}
}
静态类程序{
静态void Main(){
WriteLine(“A.Instance=(A={0},b={1})”,A.Instance.A,A.Instance.b);
}
}

您只能在编译时检测到编译器错误。

我认为您只是误读了最后一句话。规范明确规定实例字段初始值设定项不能引用正在创建的实例。然后,它只是引用一些例子。您不能使用
this
,同样的原因,您也不能使用“简单名称”,因为简单名称访问隐式使用
this
。规范并没有缩小范围。它只是简单地说出一些具体的违法建筑。另一种方法是使用
base
从基类访问受保护的字段。

根据,如果您将i声明为
const int i=5,那么简单的名称=identifier.FWIW,您可以将其用作数组初始值设定项…@JerKimball,这与手头的问题无关,也很明显是OP已经知道的东西。@Servy Ah-mea culpa,误读了问题。(羞耻)你在这里分析规范太多了。您所指的条款旨在澄清
this.x
(其中
this
是明确的)和作为
this.x
别名的简单名称
x
都是非法的。规范不能简单地说“使用
这个
是非法的”,因为这样就不清楚通过简单名称隐式使用也是非法的。但是,简单地说引用任何实例成员都是非法的就足够了,不是吗?既然更广泛的案件从一开始就是非法的,为什么还要如此狭隘地加以规定?您可能是对的,但如果是这样的话,那么指定所有可能的场景似乎是一种不必要的复杂方式。实例字段初始值设定项确实可以使用另一个实例字段;一个有效的例子
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);
  }
}