Ada*_*ite 7 generics protocols ios type-alias swift
作为学习练习,我在Swift中重写我的验证库.
我有一个ValidationRule协议,定义了各个规则应该是什么样子:
protocol ValidationRule {
typealias InputType
func validateInput(input: InputType) -> Bool
//...
}
Run Code Online (Sandbox Code Playgroud)
关联类型InputType定义要验证的输入类型(例如String).它可以是显式的或通用的.
这有两条规则:
struct ValidationRuleLength: ValidationRule {
typealias InputType = String
//...
}
struct ValidationRuleCondition<T>: ValidationRule {
typealias InputType = T
// ...
}
Run Code Online (Sandbox Code Playgroud)
在其他地方,我有一个函数来验证带有ValidationRules 集合的输入:
static func validate<R: ValidationRule>(input i: R.InputType, rules rs: [R]) -> ValidationResult {
let errors = rs.filter { !$0.validateInput(i) }.map { $0.failureMessage }
return errors.isEmpty ? .Valid : .Invalid(errors)
}
Run Code Online (Sandbox Code Playgroud)
我认为这会起作用,但编译器不同意.
在下面的例子中,即使输入是一个字符串,rule1的InputType是一个字符串,而rule2小号InputType是一个String ...
func testThatItCanEvaluateMultipleRules() {
let rule1 = ValidationRuleCondition<String>(failureMessage: "message1") { $0.characters.count > 0 }
let rule2 = ValidationRuleLength(min: 1, failureMessage: "message2")
let invalid = Validator.validate(input: "", rules: [rule1, rule2])
XCTAssertEqual(invalid, .Invalid(["message1", "message2"]))
}
Run Code Online (Sandbox Code Playgroud)
...我收到了非常有用的错误消息:
_不可转换为ValidationRuleLength
哪个是神秘的,但暗示类型应该完全相同?
所以我的问题是......如何将所有符合具有关联类型的协议的不同类型附加到集合中?
不确定如何实现我正在尝试的,或者甚至是否可能?
编辑
这是没有上下文的:
protocol Foo {
typealias FooType
func doSomething(thing: FooType)
}
class Bar<T>: Foo {
typealias FooType = T
func doSomething(thing: T) {
print(thing)
}
}
class Baz: Foo {
typealias FooType = String
func doSomething(thing: String) {
print(thing)
}
}
func doSomethingWithFoos<F: Foo>(thing: [F]) {
print(thing)
}
let bar = Bar<String>()
let baz = Baz()
let foos: [Foo] = [bar, baz]
doSomethingWithFoos(foos)
Run Code Online (Sandbox Code Playgroud)
我们得到:
Protocol Foo只能用作通用约束,因为它具有Self或相关类型要求.
我明白那个.我需要说的是:
doSomethingWithFoos<F: Foo where F.FooType == F.FooType>(thing: [F]) {
}
Run Code Online (Sandbox Code Playgroud)
Rob*_*ier 12
具有类型别名的协议不能以这种方式使用.Swift没有办法直接谈论类型ValidationRule或类型的元类型Array.你只能处理像ValidationRule where...或的实例化Array<String>.使用typealiases,没有办法直接到达那里.所以我们必须通过类型擦除间接到达那里.
Swift有几种类型的擦除器.AnySequence,AnyGenerator,AnyForwardIndex,等等,这些都是协议的仿制版本.我们可以建立自己的AnyValidationRule:
struct AnyValidationRule<InputType>: ValidationRule {
private let validator: (InputType) -> Bool
init<Base: ValidationRule where Base.InputType == InputType>(_ base: Base) {
validator = base.validate
}
func validate(input: InputType) -> Bool { return validator(input) }
}
Run Code Online (Sandbox Code Playgroud)
这里的深刻魔力是validator.有可能还有其他方法可以在没有闭包的情况下进行类型擦除,但这是我所知道的最佳方式.(我也讨厌Swift无法处理validate成为闭包属性的事实.在Swift中,属性getter不是正确的方法.所以你需要额外的间接层validator.)
有了这个,你可以制作你想要的那种阵列:
let len = ValidationRuleLength()
len.validate("stuff")
let cond = ValidationRuleCondition<String>()
cond.validate("otherstuff")
let rules = [AnyValidationRule(len), AnyValidationRule(cond)]
let passed = rules.reduce(true) { $0 && $1.validate("combined") }
Run Code Online (Sandbox Code Playgroud)
请注意,类型擦除不会丢弃类型安全.它只是"擦除"了一层实现细节.AnyValidationRule<String>仍然不同AnyValidationRule<Int>,所以这将失败:
let len = ValidationRuleLength()
let condInt = ValidationRuleCondition<Int>()
let badRules = [AnyValidationRule(len), AnyValidationRule(condInt)]
// error: type of expression is ambiguous without more context
Run Code Online (Sandbox Code Playgroud)