作为学习的练习,我将在Swift中重写我的验证库。
我有一个ValidationRule
协议定义了各个规则的外观:
protocol ValidationRule {
typealias InputType
func validateInput(input: InputType) -> Bool
//...
}
关联的类型InputType
定义要验证的输入的类型(例如,字符串)。它可以是显式的或通用的。
这是两个规则:
struct ValidationRuleLength: ValidationRule {
typealias InputType = String
//...
}
struct ValidationRuleCondition<T>: ValidationRule {
typealias InputType = T
// ...
}
在其他地方,我有一个函数,用于验证带有ValidationRule
s 集合的输入:
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)
}
我以为这行得通,但是编译器不同意。
在下面的例子中,即使输入是一个字符串,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"]))
}
…我收到了非常有用的错误消息:
_无法转换为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)
在这里我们得到:
协议Foo只能用作通用约束,因为它具有Self或关联的类型要求。
我明白那个。我需要说的是这样的:
doSomethingWithFoos<F: Foo where F.FooType == F.FooType>(thing: [F]) {
}
带有类型别名的协议不能以这种方式使用。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) }
}
这里的深奥魔力是validator
。可能还有其他方法可以不关闭而进行类型擦除,但这是我所知道的最好方法。(我也讨厌Swift无法将validate
其作为闭包属性来处理。在Swift中,属性获取器不是正确的方法。因此,您需要额外的间接层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") }
请注意,类型擦除不会放弃类型安全性。它只是“擦除”实现细节的一层。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
问题内容: 编译错误如下: 类型“ AnyObject”不符合协议“ SequenceType” 这种压力是什么? 谁能帮我很多忙! 问题答案: 苹果在Swift编程语言中指出: for-in循环针对范围,序列,集合或进度中的每个项目执行一组语句。 目前,它仅符合protocol ,因此您无法在其上使用for循环。如果要这样做,则必须执行类似以下操作:
问题内容: Beta 3一切正常,现在出现一个奇怪的错误,而且我不知道如何解决它。尝试了所有类似问题的解决方案。 这是我的代码: 两条标记线都给了我相同的错误: 类型“ String.Index”不符合协议“ IntegerLiteralConvertible” 有人能帮我吗?还是Beta 4有漏洞?谢谢! 问题答案: 在Beta 4中,Swift的String.Index处理再次发生了变化- 现
问题内容: 尝试按照Apple文档(和教程化的)创建Launch Helper时,我似乎遇到了麻烦,原因是将Objective- C代码移植到Swift中…在此方面,谁的编译器再也不过分了案件。 该错误似乎始终是: 我尝试过在多个位置进行转换,以防万一我只是在处理一个多余的,古老的原语(由Obj- C或Core Foundation引入)而无济于事。 为了以防万一,我尝试投射响应: 产生错误: …
我知道重载是在编译时决定的,但当我试图运行下面的示例时,它给出了我无法理解的结果 当我每次运行这个代码片段时,我都会得到“Collection”的输出,这意味着调用参数为Collection的classify方法。 请解释
问题内容: 我有一个符合多种协议的Objective-C变量。 我将如何在Swift中代表这种类型? 问题答案: 这应该工作: 注意,必须快速使用NSObjectProtocol而不是NSObject。 以下是一些其他示例: 符合多种协议的对象数组: 具有符合多种协议的参数的功能: 对于3.1之前的Swift版本,请使用:
我已经找了一段时间了,但是找不到一个明确的答案。 很多人说使用联合来键入双关语是不明确的,也是不好的做法。这是为什么呢?我看不出它会做任何未定义的事情的任何理由,考虑到你写入原始信息的内存不会自动改变(除非它超出了堆栈的范围,但这不是一个联合问题,这将是一个糟糕的设计)。 人们引用严格的混淆现象规则,但在我看来,这就像说你做不到,因为你做不到。 此外,如果不打双关语,工会还有什么意义?我在某个地方