ios – 如何将符合具有关联类型的协议的不同类型添加到集合中?

前端之家收集整理的这篇文章主要介绍了ios – 如何将符合具有关联类型的协议的不同类型添加到集合中?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
作为学习练习,我在 Swift中重写了我的 validation library.

我有一个ValidationRule协议,它定义了各个规则应该是什么样子:

@H_403_4@protocol ValidationRule { typealias InputType func validateInput(input: InputType) -> Bool //... }

关联类型InputType定义要验证的输入类型(例如String).它可以是显式的或通用的.

这有两条规则:

@H_403_4@struct ValidationRuleLength: ValidationRule { typealias InputType = String //... } struct ValidationRuleCondition<T>: ValidationRule { typealias InputType = T // ... }

在其他地方,我有一个函数来验证带有ValidationRules集合的输入:

@H_403_4@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) }

我认为这会起作用,但编译器不同意.

在下面的示例中,即使输入是String,rule1的InputType也是String,而rule2s的InputType是String …

@H_403_4@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"])) }

…我收到了非常有用的错误消息:

_ is not convertible to ValidationRuleLength

哪个是神秘的,但暗示类型应该完全相同?

所以我的问题是……如何将所有符合具有关联类型的协议的不同类型附加到集合中?

不确定如何实现我正在尝试的,或者甚至是否可能?

编辑

这是没有上下文的:

@H_403_4@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)

我们得到:

Protocol Foo can only be used as a generic constraint because it has
Self or associated type requirements.

我明白那个.我需要说的是:

@H_403_4@doSomethingWithFoos<F: Foo where F.FooType == F.FooType>(thing: [F]) { }

解决方法

具有类型别名的协议不能以这种方式使用. Swift没有办法直接讨论像ValidationRule或Array这样的元类型.您只能处理ValidationRule中的实例化…或者Array< String>.使用typealiases,没有办法直接到达那里.所以我们必须通过类型擦除间接到达那里.

Swift有几种类型的擦除器. AnySequence,AnyGenerator,AnyForwardIndex等.这些是协议的通用版本.我们可以构建自己的AnyValidationRule:

@H_403_4@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) } }

这里的深刻魔法是验证者.有可能还有其他方法可以在没有闭包的情况下进行类型擦除,但这是我所知道的最佳方式. (我也讨厌Swift无法处理validate作为闭包属性这一事实.在Swift中,属性getter不是正确的方法.所以你需要额外的验证器间接层.)

有了这个,你可以制作你想要的那种阵列:

@H_403_4@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<字符串>仍然与AnyValidationRule< Int>不同,所以这将失败:

@H_403_4@let len = ValidationRuleLength() let condInt = ValidationRuleCondition<Int>() let badRules = [AnyValidationRule(len),AnyValidationRule(condInt)] // error: type of expression is ambiguous without more context
原文链接:https://www.f2er.com/iOS/330772.html

猜你在找的iOS相关文章