使用通用约束
Working with generic constraints
我知道以前有人问过这个问题,但我不知道如何解决当前的问题。我已经用 associatedtype
属性:
定义了一个协议 MultipleChoiceQuestionable
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 警告我:Protocol MultipleChoiceQuestionable can only be used as a generic constraint. 我一直在寻找如何解决这个问题问题,因为泛型对我来说很新。显然 Swift 在编译期间不知道 associatedtype
的类型,这就是引发此错误的原因。我读过有关使用类型擦除的信息,但我不知道这是否能解决我的问题。也许我应该改用通用属性,或者我的协议定义有误?
如果你想对你的子协议对象应用的操作不依赖于关联类型(即既没有通用参数也没有 returns 通用类型)你可以引入一个辅助协议,它只是公开你需要的properties/methods,让你的类型符合那个协议,并根据那个协议声明question
。
例如,如果您只想了解有关该问题的一些信息:
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)
}
正如我所说,如果您不能提供抽象(非通用)接口,那么这将不起作用。
编辑
如果您需要处理问题中的通用数据,您可以根据具体的通用类型将逻辑提取到另一种 "processing" 类型中,该类型为您的问题提供接口。然后每个问题类型将其数据分派到处理器接口:
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)
}
顺便说一句,如果您在实际应用程序中没有更多的协议和结构,您也可以完全放弃协议的东西……对于这种问题,它似乎有点过度设计。
我知道以前有人问过这个问题,但我不知道如何解决当前的问题。我已经用 associatedtype
属性:
MultipleChoiceQuestionable
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 警告我:Protocol MultipleChoiceQuestionable can only be used as a generic constraint. 我一直在寻找如何解决这个问题问题,因为泛型对我来说很新。显然 Swift 在编译期间不知道 associatedtype
的类型,这就是引发此错误的原因。我读过有关使用类型擦除的信息,但我不知道这是否能解决我的问题。也许我应该改用通用属性,或者我的协议定义有误?
如果你想对你的子协议对象应用的操作不依赖于关联类型(即既没有通用参数也没有 returns 通用类型)你可以引入一个辅助协议,它只是公开你需要的properties/methods,让你的类型符合那个协议,并根据那个协议声明question
。
例如,如果您只想了解有关该问题的一些信息:
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)
}
正如我所说,如果您不能提供抽象(非通用)接口,那么这将不起作用。
编辑
如果您需要处理问题中的通用数据,您可以根据具体的通用类型将逻辑提取到另一种 "processing" 类型中,该类型为您的问题提供接口。然后每个问题类型将其数据分派到处理器接口:
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)
}
顺便说一句,如果您在实际应用程序中没有更多的协议和结构,您也可以完全放弃协议的东西……对于这种问题,它似乎有点过度设计。