编译错误。 使用带有struct的属性

请解释struct构造函数的以下错误。 如果我将struct更改为class,那么错误就会消失。

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”的备份字段。 考虑从构造函数初始化程序中调用默认构造函数。

正如错误消息建议的那样,您可以通过从构造函数初始值设定项中调用默认构造函数来解决此问题。

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

从语言规范:

10.7.3自动实现的属性

将属性指定为自动实现的属性时,将为该属性自动提供隐藏的后备字段,并且实现访问器以读取和写入该后备字段。 […]由于支持字段不可访问,因此只能通过属性访问器读取和写入,即使在包含类型中也是如此。 […] 此限制还意味着只能使用结构的标准构造函数来实现具有自动实现属性的结构类型的明确赋值,因为分配给属性本身需要明确赋值结构。 这意味着用户定义的构造函数必须调用默认构造函数。

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

请注意,您在那里的结构是可变的。 不建议这样做 。 我建议您将类型设为类(您的编译问题应立即消失)或使类型不可变。 最简单的方法是,假设您提供的代码是整个结构,将使setter私有( get; private set; )。 当然,您还应该确保之后不依赖于私有访问来修改字段的结构中添加任何变异方法。 或者,您可以使用readonly备份字段来支持属性,并完全删除setter。

您拥有的代码等同于以下代码:

 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); // ie null or default depending on whether this is reference or value type. this._irc = default(int); // ie 0 } public DealImportRequest(DealRequestBase drb) { this.DealReq = drb; this.ImportRetryCounter = 0; } } 

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

  1. 实现自动属性。
  2. 确定哪些成员相对this处理。
  3. 给所有struct sa默认的无参数构造函数。

前两个是可选的(你可以根据需要明确地编写它们)但第三个不是 – 我们不允许为struct的无参数构造函数编写我们自己的代码,我们必须使用一个类似于在上面的代码中自动给我们。

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

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

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

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

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