为什么我必须用非默认构造函数初始化C#struct中的所有字段?

为什么我必须用非默认构造函数初始化C#struct中的所有字段?,c#,struct,clr,stack,C#,Struct,Clr,Stack,我想尝试以下代码: public struct Direction { private int _azimuth; public int Azimuth { get { return _azimuth; } set { _azimuth = value; } } public Direction(int azimuth) { Azimuth = azimuth } } 但编译失败,我知道struct

我想尝试以下代码:

public struct Direction
{
   private int _azimuth;

   public int Azimuth
   {
     get { return _azimuth; }
     set { _azimuth = value; }
   }       

   public Direction(int azimuth)
   { 
      Azimuth = azimuth
   } 
}
但编译失败,我知道struct需要初始化它的所有字段。 但我试图了解在CLR\IL罩下会发生什么。 为什么在任何其他方法\property\this等之前需要所有字段


谢谢。

您需要初始化字段,而不是通过属性。

这是因为结构是从System.ValueType派生的,而不是从System.Object派生的。System.ValueType实现了默认构造函数,您不能覆盖它。此默认构造函数使用默认值初始化结构中的所有字段。因此,如果要在类中实现任何参数构造函数,还需要确保调用system.ValueType默认常量。要回答为什么它需要初始化它的所有值,这是因为值存储在堆栈内存中。

这是有效的:

public struct Direction
{
    public int Azimuth { get; private set; }

    public Direction(int azimuth) : this()
    {
        Azimuth = azimuth;
    }
}
  public Direction(int azimuth)
  {
    _azimuth = azimuth;
  }
根据规范:

使用调用结构构造函数 新的接线员,但那不是 暗示内存正在被分配。 而不是动态地分配 对象并返回对的引用 它只是一个结构构造函数 返回结构值本身 (通常在道路上的临时位置) 堆栈),然后将该值 必要时复制


基本上,编译器必须看到构造函数中的每个字段都已初始化,以便它可以复制这些值,并且它不愿意检查对函数或属性的调用。

我刚刚在中找到一个解释,说明此规则是强制的,因为如果使用无默认构造函数,将跳过内存清零。因此,您必须为所有字段提供初始化值,以避免某些字段包含随机值。通过调用无参数的默认构造函数可以很容易地实现这一点,但代价是初始化某些字段两次

我说不出这个解释是否正确,但听起来很合理

定义非默认初始值设定项时,C#要求您设置所有字段,因为它 跳过内存的归零,并允许您对其进行初始化-否则您必须有一个 双重初始化性能命中。如果你不在乎(非常轻微) 性能影响始终可以链接对:this()初始值设定项的调用,然后 初始化所选字段


值类型是在堆栈上创建的(除非嵌套在引用类型中)。关于堆栈上的字段/位置,CLR不能保证它们将被清零(与托管堆上保证清零的字段/位置相反)。因此,在阅读之前必须先将其写入。否则就是一个安全漏洞

结构的默认ctor(不带参数,不允许显式指定)将结构的所有字段置零,因此可以在执行此操作后使用结构

new BimonthlyPairStruct()
但是,在实现参数化ctor时,必须确保所有字段都已初始化,这是CLR将代码作为安全的/已验证的传递所必需的


另请参见:CLR via C#2nd Ed-第188页

嘿,我不是在寻找“如何正确书写”—我在寻找一个解释,为什么……嘿,我不是在寻找“如何正确书写”-我正在寻找一个解释,为什么……将尚未初始化的变量或字段作为
out
参数传递给外部或虚拟方法是合法的,即使编译器需要确保在这样做之前对其进行初始化(另一种方法可以使用允许在写入参数之前读取
out
参数的语言编写)。由于编译器必须在该场景中插入初始化代码,因此几乎可以肯定,如果编译器的实现者如此倾向于插入初始化代码,那么它必须能够在任何场景中插入初始化代码。奇怪的是,为什么C#compiler不隐式插入
:this()
在这种情况下?@Vlad,因为字段可能会初始化两次。一次由基本构造函数调用,另一次由用户的构造函数初始化(如果用户要初始化字段).Daniel Brückner在这里有一个解释这一点的答案。有趣的是,尽管形式为
structVar=structExpr
的表达式通常会用后者的相应字段覆盖前一个结构的所有字段,但
structVar=new StructType(params)的情况并非如此该形式的表达式有时会生成一个临时变量,将其传递给构造函数,然后将其复制到指定的变量,但有时它们会简单地调用构造函数,受影响的变量会作为
ref
参数有效地传递(该参数不会先归零)。