使用通用约束

时间:2018-02-07 08:34:55

标签: swift generics swift-protocols

我知道之前已经问过这个问题,但我不知道如何解决当前这个问题。我已经使用MultipleChoiceQuestionable属性定义了一个协议associatedtype

protocol Questionable {
    var text: String {get set}
    var givenAnswer: String? {get set}
}

protocol MultipleChoiceQuestionable: Questionable {
    associatedtype Value
    var answers: Value { get }
}

struct OpenQuestion: Questionable {
    var text: String
    var givenAnswer: String?
}

struct MultipleChoiceQuestion: MultipleChoiceQuestionable {
    typealias Value = [String]
    var text: String
    var givenAnswer: String?
    var answers: Value
}

struct NestedMultipleChoiceQuestion: MultipleChoiceQuestionable {
    typealias Value = [MultipleChoiceQuestion]
    var text: String
    var answers: Value
    var givenAnswer: String?
} 

符合此协议的类型以Questionable形式保存在数组中,如下所示:

// This array contains OpenQuestion, MultipleChoiceQuestion and NestedMultipleChoiceQuestion
private var questions: [Questionable] = QuestionBuilder.createQuestions()

我的代码中的某处我想做类似的事情:

let question = questions[index]
if let question = question as? MultipleChoiceQuestionable { 
   // Do something with the answers
      question.answers = .....
}

这是不可能的,因为Xcode警告我:协议MultipleChoiceQuestionable只能用作通用约束。我一直在寻找如何解决这个问题,因为泛型对我来说很新。显然,Swift在编译期间不知道associatedtype的类型,这就是抛出此错误的原因。我已经读过关于使用类型擦除但我不知道这是否解决了我的问题。也许我应该使用通用属性,或者我的协议定义错了?

1 个答案:

答案 0 :(得分:1)

如果要应用于子协议对象的操作不依赖于关联类型(即既没有通用参数也没有返回泛型类型),则可以引入一个辅助协议,它只公开属性/方法你需要,让你的类型符合该协议,并根据该协议声明protocol MultipleChoiceInfo { var numberOfAnswers: Int { get } } extension MultipleChoiceQuestion: MultipleChoiceInfo { var numberOfAnswers: Int { return answers.count } } // do the same for the other multiple-choice types

例如,如果您只想了解有关该问题的一些信息:

let question = questions[index]
if let info = question as? MultipleChoiceInfo {
  print(info.numberOfAnswers)
}

然后您可以通过新协议访问问题,如下所示:

protocol MultipleChoiceProcessor {
  func process(stringAnswers: [String])
  func process(nestedAnswers: [MultipleChoiceQuestion])
}

protocol MultipleChoiceProxy {
  func apply(processor: MultipleChoiceProcessor)
}

extension MultipleChoiceQuestion: MultipleChoiceProxy {
  func apply(processor: MultipleChoiceProcessor) {
    processor.process(stringAnswers: answers)
  }
}

正如我所说,如果你不能提供一个抽象的(非通用的)界面,那么这不会起作用。

修改

如果您需要处理问题中的通用数据,您可以根据具体的通用类型将逻辑提取到另一个"处理"类型,提供您的问题的接口。然后,每个问题类型将其数据分派到处理器接口:

MultipleChoiceProcessor

只需创建符合if let proxy = question as? MultipleChoiceProxy { proxy.apply(processor:myProcessor) } 的类型,然后再次进行类型检查:

telnet demo.wftpserver.com 2222
Trying 199.71.215.197...
Connected to demo.wftpserver.com.
Escape character is '^]'.
SSH-2.0-WingFTPServer

顺便说一句,如果你在实际应用程序中没有更多的协议和结构,你可能也会完全放弃协议的东西......对于这类问题,它似乎有点过度设计。 / p>