swift 用存储的属性覆盖计算属性

ecfsfe2w  于 2023-02-28  发布在  Swift
关注(0)|答案(2)|浏览(136)

假设我有一个类A和B,如下所示:

class A {
    var x: Int { return 9 }
    func p() {  print(x) }
}

class B: A {
    ...
}

let v: A = B()
v.p()

然后我用三种不同的方式覆盖B中的x:

class B: A {

    // OPTION 1: FINE - prints 10
    override var x: Int { return 10 }

    // OPTION 2: ERROR - cannot override with a stored property 'x'
    override var x = 10

    // OPTION 3: FINE - prints 10
    override lazy var x = 10

}

有人能解释一下用存储属性覆盖计算属性有什么问题吗?为什么当我声明它是懒惰的时候,所有的东西都突然固定了?

vh0rcniy

vh0rcniy1#

没有真正的技术原因可以解释为什么不能用存储的属性重写属性;它有getter和setter(访问器),可以覆盖要覆盖的属性的访问器。2它应该只是等价于用一个计算属性覆盖,然后转发到一个存储属性。
不过,我确实预见到允许这样做会有一个小小的困难:属性观察器重写。
请看下面的例子:

class C {
  var f: Int {
    get { return 5 }
    set { print("C says f was set") }
  }
}

class D : C {
  override var f: Int {
    didSet {
      print("D says f was set")
    }
  }
}

let d = D()
print(d.f)
d.f = 7
print(d.f)

// 5
// C says f was set
// D says f was set
// 5

我们可以在D中重写fdidSet,在这种情况下,override var f: Int本质上被视为一个计算属性,带有一个getter和setter,它转发到super,并在setter中对didSet实现进行额外的调用。
这里D没有引入实际的 * storage *。那么为什么会有问题呢?那么,我们如何告诉编译器我们 * do * 实际上需要为f存储?添加一个初始化表达式(= 10)可以传达这一点,但不是所有存储的属性都有默认值;大多数都是从类的指定初始化器初始化的。我们可能需要某种属性,但是对于这样一个有限的用例,这看起来对语言来说不是一个特别有用的改变。
lazy的情况很清楚,因为我们不能向它们添加属性观察器。
尽管如此,你所陈述的具体情况也应该是明确的;因为基本属性只有一个getter,所以没有属性观察器可以覆盖。我建议你file a bug(希望)看看Swift团队对此有什么看法。
使用计算属性重写(然后转发到存储属性)始终可以获得相同的结果,但:

class C {
  var f: Int {
    return 9
  }
}

class D : C {

  private var _f: Int = 10

  override var f: Int {
    get { return _f }
    set { _f  = newValue }
  }
}

let d = D()
print(d.f) // 10
d.f = 7
print(d.f) // 7
kmb7vmvb

kmb7vmvb2#

不能用可写存储属性重写只读计算属性。
具有getter但没有setter的计算属性称为只读计算属性。只读计算属性始终返回一个值,并且可以通过点语法访问,但不能设置为其他值。
文件

相关问题