【问题标题】:Struct initialization and new operator结构初始化和新操作符
【发布时间】:2013-11-26 19:43:33
【问题描述】:

我在 C# 中有两个类似的结构,每个都包含一个整数,但后者实现了 get/set 访问器。

为什么在分配a 字段之前必须使用new 运算符初始化Y 结构?当我用new 初始化它时,y 仍然是一个值类型吗?

public struct X
{
    public int a;
}

public struct Y
{
    public int a { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        X x;
        x.a = 1;

        Y y;
        y.a = 2; // << compile error "unused local variable" here

        Y y2 = new Y();
        y2.a = 3;
    }
}

【问题讨论】:

  • 我有点惊讶X x; x.a = 1; 工作......
  • Mutable structs are evil。我对 C# 非常熟悉,但我完全希望您使用 X 的示例无法编译。确实如此,很奇怪。但是,X x; Console.WriteLine(x);X x; x.a = 1; Console.WriteLine(x);有效。
  • 另外,你不应该有非私有字段,你应该将它们设为属性(如Y)。
  • 我认为我们需要 Jon Skeet 或 Eric Lippert 来解释这个魔法。看起来将所有字段都分配为初始化。但在第二种情况下,它是调用未初始化结构的方法。
  • 规范来了! “12.3 明确分配”(ecma-334 的第 122 页)。 “如果一个结构类型变量的每个实例变量都被认为是明确分配的,那么它就被认为是明确分配的”

标签: c# .net struct


【解决方案1】:

一个有效而另一个无效的原因是您不能在未初始化的对象上调用方法。属性设置器也是方法。

public struct X
{
    public int a;
    public void setA(int value)
    { this.a = value; }
}

public struct Y
{
    public int a { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        X x;
        x.setA(1); // A: error
        x.a = 2; // B: okay

        Y y;
        y.a = 3; // C: equivalent to A
    }
}

不允许的原因是属性设置器可以观察到对象的未初始化状态。调用者不知道属性设置器是仅仅设置一个字段,还是做更多的事情。

【讨论】:

  • 这很奇怪。是否允许 A B 之后?如果是这样,如果 X 有另一个成员变量怎么办?如果没有,那么允许设置未初始化的struct 的成员有什么意义?
  • @BlueRaja-DannyPflughoeft 是的,允许 B 之后的 A,不,如果 X 有另一个字段,则不允许。当一个结构的所有字段都被赋值后,整个结构就被认为是初始化了。
  • 真的!?如果struct 成员设置在方法或 lambda 中怎么办?编译器是否认真追踪哪些方法改变了哪些结构成员?如果不是,如果结构是在构造函数中初始化的成员变量怎么办?这似乎有太多特殊情况不符合“保持简单,愚蠢”的C#设计理念
  • @BlueRaja-DannyPflughoeft 如果结构未完全初始化,则不能将其传递给另一个方法(out 除外),所以不,编译器不需要跟踪其他方法的作用。并且类字段,即使它们是结构类型,也总是被初始化,即使类的构造函数没有设置它们。
【解决方案2】:

在第一种情况下,您只需分配字段。它不涉及结构的实际使用,只是将值设置到内存中(结构地址+堆栈上的字段偏移量)。

在第二种情况下,您调用方法set_a(int value),但由于变量未初始化而失败。

在第三种情况下,构造函数会为你初始化它,所以使用变量是可以的。

更新:规范来了!

“12.3 明确分配”(ecma-334 的第 122 页)。

如果一个结构类型变量的每个实例变量都被认为是肯定赋值的,那么它就被认为是肯定赋值的

【讨论】:

    【解决方案3】:

    这在C# specification section 5.3 dealing with "Definite Assignment":

    如果一个struct-type变量的每个实例变量都被认为是绝对赋值的,那么它就被认为是绝对赋值的。

    和:

    如果通向该位置的所有可能执行路径至少包含以下之一,则认为最初未分配的变量(第 5.3.2 节)在给定位置已确定分配:
    * 一个简单的赋值(第 7.13.1 节),其中变量是左操作数。
    * ...

    因此,这也有效:

    void Main()
    {
        X x;
        x.a = 1;
        x.b = 2;
    
        x.Dump();
    }
    
    public struct X
    {
        public int a;
        public int b;
    }
    

    您可以在LINQPad 中进行测试。

    请注意,如果您在其上调用代码,编译器无法证明结构类型变量被认为是绝对赋值的,这就是您对属性所做的事情。因此,在您可以使用结构类型变量的属性之前,必须明确地分配它。

    【讨论】:

      【解决方案4】:

      值类型的new 运算符运行指定的构造函数。与引用类型不同,这是可选的,因此如果您不使用new,默认构造函数将隐式运行(您无法指定自己的默认构造函数,因此它始终具有为字段赋予默认值的效果它们的类型)。

      至于为什么编译器出错,我不太确定。有趣的是,在 C# 交互窗口中,

      public struct Y
      {
          public int a { get; set; }
      }
      Y test;
      test.a = 5;
      

      工作得很好。

      【讨论】:

      • 这不能回答问题。为什么允许x.a = 1 而不允许y.a = 2
      • "so if you don't use new, the default constructor doesn't get run." 这不是真的。它将隐式运行。
      猜你喜欢
      • 1970-01-01
      • 2011-09-21
      • 1970-01-01
      • 1970-01-01
      • 2012-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-16
      相关资源
      最近更新 更多