为什么协议中的 get-only 属性 要求不能被符合的 属性 满足?
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
为什么下面的代码会产生错误?
protocol ProtocolA {
var someProperty: ProtocolB { get }
}
protocol ProtocolB {}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA { // Type 'SomeClass' does not conform to protocol 'ProtocolA'
var someProperty: ConformsToB
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
}
有道理。但是,在我的示例中,属性 是只获取的。为什么这行不通?这是 Swift 的缺点,还是有什么理由说得通?
没有真正的理由为什么这不应该成为可能,只读 属性 要求 可以 是协变的,因为 returning ConformsToB
来自 属性 的实例,类型为 ProtocolB
是完全合法的。
Swift只是暂时不支持。为此,编译器必须生成 a thunk between the protocol witness table and conforming implementation in order to perform the necessary type-conversion(s). For example, a ConformsToB
instance would need to be boxed in an existential container 才能被键入为 ProtocolB
(并且调用者无法做到这一点,因为它可能对被调用的实现一无所知).
但是,编译器没有理由不能这样做。 this one which is specific to read-only property requirements, and this general one 有多个错误报告公开,其中 Swift 团队成员 Slava Pestov 说:
[...] we want protocol witnesses and method overrides in every case where a function conversion is allowed
所以它看起来确实像是 Swift 团队希望在该语言的未来版本中实现的东西。
然而与此同时,作为 ,一种解决方法是使用 associatedtype
:
protocol ProtocolA {
// allow the conforming type to satisfy this with a concrete type
// that conforms to ProtocolB.
associatedtype SomeProperty : ProtocolB
var someProperty: SomeProperty { get }
}
protocol ProtocolB {}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA {
// implicitly satisfy the associatedtype with ConformsToB.
var someProperty: ConformsToB
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
}
但这并不令人满意,因为这意味着 ProtocolA
不再可用作类型(因为它有 associatedtype
要求)。它还改变了协议的内容。最初它说 someProperty
可以 return 任何符合 ProtocolB
的东西 – 现在它说 someProperty
的实现只处理一个 specific 符合 ProtocolB
.
的具体类型
另一种解决方法是定义一个虚拟 属性 以满足协议要求:
protocol ProtocolA {
var someProperty: ProtocolB { get }
}
protocol ProtocolB {}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA {
// dummy property to satisfy protocol conformance.
var someProperty: ProtocolB {
return actualSomeProperty
}
// the *actual* implementation of someProperty.
var actualSomeProperty: ConformsToB
init(someProperty: ConformsToB) {
self.actualSomeProperty = someProperty
}
}
这里我们实际上是在为 编译器编写 thunk ——但它也不是特别好,因为它向 API 添加了一个不必要的 属性 .
除了 Harmish 的出色响应之外,如果您想在 SomeClass
和 ProtocolA
上继续使用相同的 属性 名称,您可以
protocol ProtocolB {}
protocol ProtocolA {
var _someProperty_protocolA: ProtocolB { get }
}
extension ProtocolA {
var someProperty: ProtocolB {
return _someProperty_protocolA
}
}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA {
// the *actual* implementation of someProperty.
var _someProperty: ConformsToB
var someProperty: ConformsToB {
// You can't expose someProperty directly as
// (SomeClass() as ProtocolA).someProperty would
// point to the getter in ProtocolA and loop
return _someProperty
}
// dummy property to satisfy protocol conformance.
var _someProperty_protocolA: ProtocolB {
return someProperty
}
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
}
let foo = SomeClass(someProperty: ConformsToB())
// foo.someProperty is a ConformsToB
// (foo as ProtocolA).someProperty is a ProtocolB
当您遵守另一个最初也对 someProperty
有限制的协议 ProtocolA2
时,或者当您想隐藏您的 hack 时,这会很有用 swift限制。
我现在很想知道为什么 Swift 不直接为我做这件事。
从 Swift 5.1 开始,您可以使用不透明 return 类型来引用引用另一个协议的协议,只要您还使用关联类型来这样做。
它不仅适用于只读“获取”属性,还适用于读写属性。例如,
protocol ProtocolA {
associatedtype T: ProtocolB
var someProperty: T { get }
var x: Int { get set }
}
protocol ProtocolB {
var x: Int { get set }
}
struct ConformsToB: ProtocolB {
var x: Int
}
class SomeClass: ProtocolA {
var someProperty: ConformsToB
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
var x: Int {
get {
someProperty.x
}
set {
someProperty.x = newValue
}
}
}
var protocolA: some ProtocolA = SomeClass(someProperty: ConformsToB(x: 1))
print(protocolA.x) // 1
protocolA.x = 2
print(protocolA.x) // 2
为什么下面的代码会产生错误?
protocol ProtocolA {
var someProperty: ProtocolB { get }
}
protocol ProtocolB {}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA { // Type 'SomeClass' does not conform to protocol 'ProtocolA'
var someProperty: ConformsToB
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
}
没有真正的理由为什么这不应该成为可能,只读 属性 要求 可以 是协变的,因为 returning ConformsToB
来自 属性 的实例,类型为 ProtocolB
是完全合法的。
Swift只是暂时不支持。为此,编译器必须生成 a thunk between the protocol witness table and conforming implementation in order to perform the necessary type-conversion(s). For example, a ConformsToB
instance would need to be boxed in an existential container 才能被键入为 ProtocolB
(并且调用者无法做到这一点,因为它可能对被调用的实现一无所知).
但是,编译器没有理由不能这样做。 this one which is specific to read-only property requirements, and this general one 有多个错误报告公开,其中 Swift 团队成员 Slava Pestov 说:
[...] we want protocol witnesses and method overrides in every case where a function conversion is allowed
所以它看起来确实像是 Swift 团队希望在该语言的未来版本中实现的东西。
然而与此同时,作为 associatedtype
:
protocol ProtocolA {
// allow the conforming type to satisfy this with a concrete type
// that conforms to ProtocolB.
associatedtype SomeProperty : ProtocolB
var someProperty: SomeProperty { get }
}
protocol ProtocolB {}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA {
// implicitly satisfy the associatedtype with ConformsToB.
var someProperty: ConformsToB
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
}
但这并不令人满意,因为这意味着 ProtocolA
不再可用作类型(因为它有 associatedtype
要求)。它还改变了协议的内容。最初它说 someProperty
可以 return 任何符合 ProtocolB
的东西 – 现在它说 someProperty
的实现只处理一个 specific 符合 ProtocolB
.
另一种解决方法是定义一个虚拟 属性 以满足协议要求:
protocol ProtocolA {
var someProperty: ProtocolB { get }
}
protocol ProtocolB {}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA {
// dummy property to satisfy protocol conformance.
var someProperty: ProtocolB {
return actualSomeProperty
}
// the *actual* implementation of someProperty.
var actualSomeProperty: ConformsToB
init(someProperty: ConformsToB) {
self.actualSomeProperty = someProperty
}
}
这里我们实际上是在为 编译器编写 thunk ——但它也不是特别好,因为它向 API 添加了一个不必要的 属性 .
除了 Harmish 的出色响应之外,如果您想在 SomeClass
和 ProtocolA
上继续使用相同的 属性 名称,您可以
protocol ProtocolB {}
protocol ProtocolA {
var _someProperty_protocolA: ProtocolB { get }
}
extension ProtocolA {
var someProperty: ProtocolB {
return _someProperty_protocolA
}
}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA {
// the *actual* implementation of someProperty.
var _someProperty: ConformsToB
var someProperty: ConformsToB {
// You can't expose someProperty directly as
// (SomeClass() as ProtocolA).someProperty would
// point to the getter in ProtocolA and loop
return _someProperty
}
// dummy property to satisfy protocol conformance.
var _someProperty_protocolA: ProtocolB {
return someProperty
}
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
}
let foo = SomeClass(someProperty: ConformsToB())
// foo.someProperty is a ConformsToB
// (foo as ProtocolA).someProperty is a ProtocolB
当您遵守另一个最初也对 someProperty
有限制的协议 ProtocolA2
时,或者当您想隐藏您的 hack 时,这会很有用 swift限制。
我现在很想知道为什么 Swift 不直接为我做这件事。
从 Swift 5.1 开始,您可以使用不透明 return 类型来引用引用另一个协议的协议,只要您还使用关联类型来这样做。
它不仅适用于只读“获取”属性,还适用于读写属性。例如,
protocol ProtocolA {
associatedtype T: ProtocolB
var someProperty: T { get }
var x: Int { get set }
}
protocol ProtocolB {
var x: Int { get set }
}
struct ConformsToB: ProtocolB {
var x: Int
}
class SomeClass: ProtocolA {
var someProperty: ConformsToB
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
var x: Int {
get {
someProperty.x
}
set {
someProperty.x = newValue
}
}
}
var protocolA: some ProtocolA = SomeClass(someProperty: ConformsToB(x: 1))
print(protocolA.x) // 1
protocolA.x = 2
print(protocolA.x) // 2