在Swift中,如何转换为具有关联类型的协议?

drnojrws  于 2024-01-05  发布在  Swift
关注(0)|答案(4)|浏览(146)

在下面的代码中,我想测试x是否是SpecialController。如果是,我想将currentValue作为SpecialValue获取。如何做到这一点?如果不使用强制转换,则使用其他技术。
最后一行无法编译。错误是:Protocol“SpecialController”只能用作泛型约束,因为它具有Self或关联类型要求。

  1. protocol SpecialController {
  2. associatedtype SpecialValueType : SpecialValue
  3. var currentValue: SpecialValueType? { get }
  4. }
  5. ...
  6. var x: AnyObject = ...
  7. if let sc = x as? SpecialController { // does not compile

字符串

nue99wik

nue99wik1#

不幸的是,Swift目前还不支持使用协议与关联类型作为实际类型。然而,这需要编译器is technically possible来完成;它很可能会在语言的未来版本中实现。
在您的情况下,一个简单的解决方案是定义一个SpecialController派生的“影子协议”,并允许您通过一个协议要求来访问currentValue,该协议要求可以删除它:

  1. // This assumes SpecialValue doesn't have associated types – if it does, you can
  2. // repeat the same logic by adding TypeErasedSpecialValue, and then using that.
  3. protocol SpecialValue {
  4. // ...
  5. }
  6. protocol TypeErasedSpecialController {
  7. var typeErasedCurrentValue: SpecialValue? { get }
  8. }
  9. protocol SpecialController : TypeErasedSpecialController {
  10. associatedtype SpecialValueType : SpecialValue
  11. var currentValue: SpecialValueType? { get }
  12. }
  13. extension SpecialController {
  14. var typeErasedCurrentValue: SpecialValue? { return currentValue }
  15. }
  16. extension String : SpecialValue {}
  17. struct S : SpecialController {
  18. var currentValue: String?
  19. }
  20. var x: Any = S(currentValue: "Hello World!")
  21. if let sc = x as? TypeErasedSpecialController {
  22. print(sc.typeErasedCurrentValue as Any) // Optional("Hello World!")
  23. }

字符串

展开查看全部
zzoitvuj

zzoitvuj2#

[编辑以修复:: SpecialValue,而不是= SpecialValue]
这是不可能的。SpecialValueController在概念上是一个“不完整的类型”,因此编译器无法知道。SpecialValueType,尽管它 * 受到 * SpecialValue的约束,但直到它被任何采用类确定时才知道。因此它实际上是一个信息不足的占位符。as?-ness无法检查。
如果您仍在寻求某种程度的多态性,您可以有一个采用SpecialController的基类,并为SpecialValueController提供一个 * 具体 * 类型,并且有多个继承自采用类的子类。

33qvvth1

33qvvth13#

现在可以在最新版本的Swift中这样做(用5.7测试)
范例:

  1. protocol SpecialValue {}
  2. protocol SpecialController {
  3. associatedtype SpecialValueType: SpecialValue
  4. var currentValue: SpecialValueType? { get }
  5. }
  6. extension String: SpecialValue {}
  7. struct S: SpecialController {
  8. var currentValue: String?
  9. }
  10. var x: Any = S(currentValue: "Hello World!")
  11. if let x = x as? any SpecialController {
  12. print(x.currentValue)
  13. }
  14. print(x is any SpecialController)
  15. print(type(of: x) is any SpecialController.Type)

字符串
输出量:

  1. Optional("Hello World!")
  2. true
  3. true

展开查看全部
ffx8fchx

ffx8fchx4#

这是行不通的,因为SpecialController不是一个单一的类型。你可以把关联类型看作是一种泛型。一个SpecialController,它的SpecialValueType是一个Int,与一个SpecialController,它的SpecialValueType是一个String,是一个完全不同的类型,就像Optional<Int>Optional<String>是一个完全不同的类型一样。
因此,强制转换为SpecialValueType没有任何意义,因为这会掩盖相关的类型,并允许您使用(例如)SpecialController,其SpecialValueTypeInt,而SpecialController,其SpecialValueTypeString
正如编译器所建议的,SpecialController可以被用作泛型约束的唯一方式。你可以有一个在T上泛型的函数,约束是T必须是SpecialControllerT的域现在跨越了SpecialController的所有各种具体类型,比如一个与Int关联的类型,一个是String。对于每一个可能的关联类型,都有一个不同的SpecialController,通过扩展,还有一个不同的T
为了进一步引出Optional<T>的类比。想象一下,如果你正在尝试做的事情是可能的。它会像这样:

  1. func funcThatExpectsIntOptional(_: Int?) {}
  2. let x: Optional<String> = "An optional string"
  3. // Without its generic type parameter, this is an incomplete type. suppose this were valid
  4. let y = x as! Optional
  5. funcThatExpectsIntOptional(y) // boom.

字符串

相关问题