【问题标题】:Compilation error. Using properties with struct编译错误。在结构中使用属性
【发布时间】:2010-12-02 14:00:00
【问题描述】:

请解释结构构造函数的以下错误。如果我将结构更改为类 错误消失了。

public struct DealImportRequest
{
    public DealRequestBase DealReq { get; set; }
    public int ImportRetryCounter { get; set; }

    public DealImportRequest(DealRequestBase drb)
    {
        DealReq = drb;
        ImportRetryCounter = 0;
    }
}
  • 错误 CS0188:“this”对象在其所有字段都分配给之前无法使用
  • 错误 CS0843:自动实现属性的支持字段 在将控制权返回给调用者之前,必须完全分配“DealImportRequest.DealReq”。考虑从构造函数初始化器调用默认构造函数。

【问题讨论】:

  • @Hps,我不同意。虽然它确实与该问题中的相同问题相关,但它这样做是相对于隐式字段(支持自动属性)而不是显式字段这一事实足以阻止某人理解这两个问题为何相关。这应该足以认为它们不重复 IMO。
  • 你是对的。谢谢你的解释。我应该更加小心:)

标签: c# struct compiler-errors language-features


【解决方案1】:

按照错误消息的建议,您可以通过从构造函数初始化程序调用默认构造函数来解决此问题。

public DealImportRequest(DealRequestBase drb) : this()
{
   DealReq = drb;
   ImportRetryCounter = 0;
}

来自语言规范:

10.7.3 自动实现的属性

当一个属性是 自动指定为 实现的属性,隐藏的后盾 字段自动可用 属性,访问者是 实现读取和写入 那个后备场。 [...] 因为 支持字段不可访问,它可以 只能通过读取和写入 属性访问器,即使在 包含类型。 [...] 这个 限制也意味着确定 结构类型的分配 自动实现的属性只能 使用标准实现 结构的构造函数,因为 分配给属性本身 要求结构是明确的 分配的。这意味着用户定义 构造函数必须调用默认值 构造函数。

当然,另一种(更详细的)替代方法是手动实现属性并在构造函数中自己设置支持字段。

请注意,您拥有的结构是可变的。 This is not recommended。我建议您要么将类型设置为类(您的编译问题应该立即消失)或使类型不可变。假设您提供的代码是 entire 结构,完成此操作的最简单方法是将设置器设为私有 (get; private set;)。当然,您还应该确保以后不要向结构添加任何依赖私有访问来修改字段的变异方法。或者,您可以使用 readonly 支持字段支持属性并完全摆脱设置器。

【讨论】:

    【解决方案2】:

    你的代码相当于下面的代码:

    public struct DealImportRequest
    {
        private DealRequestBase _dr;
        private int _irc;
        public DealRequestBase DealReq
        {
          get { return _dr; }
          set { _dr = value; }
        }
        public int ImportRetryCounter
        {
          get { return _irc; }
          set { _irc = value; }
        }
        /* Note we aren't allowed to do this explicitly - this is didactic code only and isn't allowed for real*/
        public DealImportRequest()
        {
            this._dr = default(DealRequestBase); // i.e. null or default depending on whether this is reference or value type.
            this._irc = default(int); // i.e. 0
        }
        public DealImportRequest(DealRequestBase drb)
        {
            this.DealReq = drb;
            this.ImportRetryCounter = 0;
        }
    }
    

    现在,我在这里所做的只是删除以下语法糖:

    1. 实现自动属性。
    2. 计算出相对于this 处理的成员。
    3. 为所有structs 提供一个默认的无参数构造函数。

    前两个是可选的(如果你愿意,你可以显式编写它们),但第三个不是 - 我们不允许为struct 的无参数构造函数编写自己的代码,我们必须使用一个就像上面代码中的代码自动提供给我们一样。

    现在,看这里,两个错误的含义突然变得清晰了——您的构造函数在分配字段之前隐式使用 this(错误 188),而这些字段是支持自动属性的字段(错误 843)。

    它是通常我们不必考虑的不同自动功能的组合,但在这种情况下效果不佳。我们可以通过遵循 843 错误消息中的建议并调用默认构造函数作为显式构造函数的一部分来解决此问题:

    public DealImportRequest(DealRequestBase drb)
        :this()
    {
        DealReq = drb;
        ImportRetryCounter = 0;
    }
    

    考虑到这与我上面的代码的扩展版本有关,您可以看到这是如何解决问题的,因为它在继续之前调用了分配给支持字段的构造函数。

    【讨论】:

      【解决方案3】:

      我建议不要对结构使用自动属性,除非您有充分的理由使用它们。将类字段包装在读写属性中很有用,因为它使实例可以控制读取或写入它的环境,并在发生读取或写入时采取行动。此外,对象实例内的代码可以识别被操作的实例,因此可以仅在读取和写入特定实例时执行特殊动作。在类的早期版本中使用自动属性将使该类的未来版本可以使用手动实现的属性,包括上述好处,同时保持与已经编译的客户端代码的兼容性。不幸的是,将结构字段包装在读写属性中并不能提供相同的好处,因为一个结构实例的字段可以复制到另一个,而任何一个实例都没有任何发言权。如果结构的语义允许在大多数情况下使用任意值写入属性[就像自动属性的情况一样],那么任何合法的替换都将在语义上等同于字段。

      【讨论】:

        猜你喜欢
        • 2017-06-18
        • 2011-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多