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

时间:2016-11-02 19:12:54

标签: swift generics swift3 protocols associated-types

在以下代码中,我想测试x是否为SpecialController。如果是,我想将currentValue作为SpecialValue。你怎么做到这一点?如果不是演员,那么其他一些技巧。

最后一行不会编译。错误是:协议“SpecialController”只能用作通用约束,因为它具有Self或关联类型要求。

protocol SpecialController {
    associatedtype SpecialValueType : SpecialValue
    var currentValue: SpecialValueType? { get }
}
...
var x: AnyObject = ...
if let sc = x as? SpecialController {  // does not compile

3 个答案:

答案 0 :(得分:14)

不幸的是,Swift目前不支持使用具有相关类型的协议作为实际类型。但是编译器要做is technically possible;它may well be implemented in a future version of the language

在您的情况下,一个简单的解决方案是定义SpecialController派生的“影子协议”,并允许您通过类型删除它的协议要求访问currentValue

// this assumes SpecialValue doesn't have associated types – if it does, you can repeat
// the same logic by adding TypeErasedSpecialValue, and then using that.
protocol SpecialValue {
  // ...
}

protocol TypeErasedSpecialController {
  var typeErasedCurrentValue: SpecialValue? { get }
}

protocol SpecialController : TypeErasedSpecialController {
  associatedtype SpecialValueType : SpecialValue
  var currentValue: SpecialValueType? { get }
}

extension SpecialController {
  var typeErasedCurrentValue: SpecialValue? { return currentValue }
}

extension String : SpecialValue {}

struct S : SpecialController {
  var currentValue: String?
}

var x: Any = S(currentValue: "Hello World!")
if let sc = x as? TypeErasedSpecialController {
  print(sc.typeErasedCurrentValue as Any) // Optional("Hello World!")
}

答案 1 :(得分:1)

[已修改:: SpecialValue,而不是= SpecialValue]

这是不可能的。 SpecialValueController是一个"不完整的类型"从概念上讲,编译器无法知道。 SpecialValueType,虽然 SpecialValue约束,但在任何采用类确定之前都不知道它。所以它是一个真正的占位符,信息不足。 as? - 无法检查。

你可以有一个基类,SpecialController采用具体类型的SpecialValueController,并且有多个子类继承自采用类,如果你&#39 ;仍然在寻求一定程度的多态性。

答案 2 :(得分:0)

这不起作用,因为SpecialController不是单一类型。您可以将关联类型视为一种泛型。其中SpecialControllerSpecialValueType的{​​{1}}与Int完全不同,其SpecialControllerSpecialValueType,就像StringOptional<Int>完全不同。

因此,转换为Optional<String>没有任何意义,因为这会掩盖关联的类型,并允许您使用(例如)SpecialValueType及其SpecialController {1}} SpecialValueType IntSpecialController的{​​{1}}为SpecialValueType

正如编译器所建议的那样,String可以使用的唯一方法是作为通用约束。您可以使用SpecialController上的泛型函数,其约束条件T必须是TSpecialController的域现在涵盖T的所有具体类型,例如SpecialController关联类型和Int。对于每种可能的关联类型,都有一个不同的String,以及一个不同的SpecialController

进一步提出T类比。想象一下,如果你想要做的事情是可能的。这将是这样的:

Optional<T>