27

在下面的代码中,我想测试是否xSpecialController. 如果是的话,我想得到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
4

3 回答 3

27

不幸的是,Swift 目前不支持将具有关联类型的协议用作实际类型。然而,这在技术上对于编译器来说是可行的;并且很可能在该语言的未来版本中实现

在您的情况下,一个简单的解决方案是定义一个“影子协议”,它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!")
}
于 2017-11-10T21:06:42.500 回答
1

[编辑修复:: SpecialValue,不是= SpecialValue]

这是不可能的。SpecialValueController在概念上是“不完整类型”,因此编译器无法知道。SpecialValueType,尽管它受 约束 SpecialValue,但直到由任何采用类确定时才知道。所以它是一个信息不足的真正占位符。as?-ness 无法检查。

如果您仍在寻求一定程度的多态性,您可以有一个采用具体类型 for 的基类SpecialController有多个从采用类继承的子类。SpecialValueController

于 2016-11-02T19:18:39.197 回答
0

这不起作用,因为SpecialController不是单一类型。您可以将关联类型视为一种泛型。ASpecialController及其SpecialValueType为 an与a为 anInt是完全不同的类型,就像一个完全不同的类型与.SpecialControllerSpecialValueTypeStringOptional<Int>Optional<String>

正因为如此,强制转换为 没有任何意义SpecialValueType,因为这会掩盖关联的类型,并允许您使用(例如) aSpecialController及其SpecialValueTypeInta SpecialController,而 a 为SpecialValueTypeaString是预期的。

正如编译器所建议的那样,唯一SpecialController可以使用的方法是作为通用约束。您可以拥有一个通用的函数 over T,其约束T必须是 a SpecialController。now的域T涵盖 的所有各种具体类型SpecialController,例如一种具有Int关联类型,一种具有String. 对于每个可能的关联类型,都有一个 distinct SpecialController,并且通过扩展,一个 distinct T

进一步引出Optional<T>类比。想象一下,如果您尝试做的事情是可能的。很像这样:

func funcThatExpectsIntOptional(_: Int?) {}

let x: Optional<String> = "An optional string"
// Without its generic type parameter, this is an incomplete type. suppose this were valid
let y = x as! Optional
funcThatExpectsIntOptional(y) // boom.
于 2016-11-02T19:18:57.303 回答