12

我想试试这段代码:

public struct Direction
{
   private int _azimuth;

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

   public Direction(int azimuth)
   { 
      Azimuth = azimuth
   } 
}

但是它在编译时失败了,我知道 struct 需要初始化它的所有字段。但我试图了解 CLR\IL 引擎盖下会发生什么。为什么它需要在任何其他方法\属性\这个等之前的所有字段。

谢谢。

4

6 回答 6

11

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

结构的默认 ctor(不接受参数并且不允许显式指定)将结构的所有字段清零,因此您可以在完成后使用结构。

new BimonthlyPairStruct()

但是,当您实现参数化 ctor 时,必须确保所有字段都已初始化 - 这是 CLR 将您的代码作为 safe/ verified传递所必需的。

另请参阅:通过 C# 第 2 版的 CLR - Pg 188

于 2009-04-25T14:02:57.220 回答
5

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

于 2009-04-06T12:57:16.937 回答
5

这有效:

  public Direction(int azimuth)
  {
    _azimuth = azimuth;
  }

从规范:

使用 new 运算符调用结构构造函数,但这并不意味着正在分配内存。结构构造函数不是动态分配对象并返回对它的引用,而是简单地返回结构值本身(通常在堆栈上的临时位置),然后根据需要复制该值。

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

于 2009-04-06T12:58:12.160 回答
5

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

我无法判断这种解释是否正确,但听起来很合理。

当您定义非默认初始化程序时,C# 要求您设置所有字段,因为它会跳过内存归零并允许您对其进行初始化 - 否则您将不得不对初始化性能造成双重打击。如果您不关心(非常轻微的)性能损失,您可以随时链接调用 : this() 初始化程序,然后只初始化选定的字段。

于 2009-04-06T13:02:52.010 回答
2
public struct Direction
{
    public int Azimuth { get; private set; }

    public Direction(int azimuth) : this()
    {
        Azimuth = azimuth;
    }
}
于 2009-04-06T12:42:54.023 回答
-1

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

于 2009-04-06T12:42:12.730 回答