通用约束
Generic constraint
我正在尝试在 Swift 中实现通用验证器。验证器只是执行一些验证,即电子邮件验证,并根据值是否有效returns true
或 false
。
protocol Validator {
associatedtype Value
func validate(_ value: Value) -> Bool
}
我还设法实现了这些验证器。如您所见,associatedValue
很有魅力。
struct EmailValidator: Validator {
func validate(_ value: String) -> Bool {
NSPredicate(format: "SELF MATCHES %@", "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}").evaluate(with: value)
}
}
struct RequiredValidator: Validator {
func validate(_ value: String) -> Bool {
!value.isEmpty
}
}
但现在我想要一个复合验证器,它不必符合 Validator
,但它确实需要接受任何 Validator
,其中 associatedValue
是相同。下面我举个例子。
let compoundValidator = CompoundValidator<String>(EmailValidator(), RequiredValidator())
但是,当我尝试实现这个想法时,我 运行 出现了诸如 Cannot specialize non-generic type 'Validator'
或 Cannot convert value of type 'Value' to expected argument type 'Validator.Value'
或 Protocol 'Validator' can only be used as a generic constraint because it has Self or associated type requirements
之类的错误,具体取决于我尝试实现的内容。有没有一种方法可以使用泛型实现这个复合验证器?
struct CompoundValidator<Value> {
let validators: [Validator<Value>]
init(_ validators: Validator<Value>...) {
self.validators = validators
}
func validate(_ value: Value) -> Bool {
validators.reduce(into: true) { partialResult, validator in
partialResult = partialResult && validator.validate(value)
}
}
}
struct CompoundValidator<Value, T: Validator> where T.Value == Value {
let validators: [T]
init(_ validators: T...) {
self.validators = validators
}
func validate(_ value: Value) -> Bool {
validators.reduce(into: true) { partialResult, validator in
partialResult = partialResult && validator.validate(value)
}
}
}
您可以创建一个 AnyValidator
类型,即具体类型。这样您就可以使用它来指定泛型类型参数。
struct AnyValidator<Value>: Validator {
private let validateFunc: (Value) -> Bool
init<T: Validator>(_ validator: T) where T.Value == Value {
validateFunc = validator.validate
}
func validate(_ value: Value) -> Bool {
validateFunc(value)
}
}
extension Validator {
func eraseToAnyValidator() -> AnyValidator<Value> {
.init(self)
}
}
struct CompoundValidator<Value> {
let validators: [AnyValidator<Value>]
init(_ validators: AnyValidator<Value>...) {
self.validators = validators
}
func validate(_ value: Value) -> Bool {
validators.reduce(into: true) { partialResult, validator in
partialResult = partialResult && validator.validate(value)
}
}
}
用法示例:
CompoundValidator(
EmailValidator().eraseToAnyValidator(),
RequiredValidator().eraseToAnyValidator())
您可能希望为少量验证器提供方便的初始化程序:
init<V1, V2>(_ v1: V1, _ v2: V2) where V1: Validator, V2: Validator, V1.Value == Value, V2.Value == Value {
self.init(v1.eraseToAnyValidator(), v2.eraseToAnyValidator())
}
init<V1, V2, V3>(_ v1: V1, _ v2: V2, _ v3: V3) where V1: Validator, V2: Validator, V3: Validator, V1.Value == Value, V2.Value == Value, V3.Value == Value {
self.init(v1.eraseToAnyValidator(), v2.eraseToAnyValidator(), v3.eraseToAnyValidator())
}
允许您省略 .eraseToAnyValidator()
。
这类似于 Combine 的 Publisher
type has a merge
运算符,它最多接受 8 个 个参数,只是为了让您可以合并不同类型的 Publisher
,而无需说 eraseToAnyPublisher
.
让我们从头开始,Validator<Value>
是无效语法,即使 Validator
是具有关联类型的协议。 Swift 还不允许存在性容器(协议引用)声明通用约束,例外是 where
子句。
因此,您需要一个类型橡皮擦才能使用任何类型的泛型:
struct AnyValidator<Value>: Validator {
private let _validate: (Value) -> Bool
init<V: Validator>(_ validator: V) where V.Value == Value {
_validate = validator.validate
}
func validate(_ value: Value) -> Bool {
_validate(value)
}
}
extension Validator {
func eraseToAnyValidator() -> AnyValidator<Value> {
AnyValidator(self)
}
}
,您需要更新定义:
struct CompoundValidator<Value> {
let validators: [AnyValidator<Value>]
init(_ validators: AnyValidator<Value>...) {
self.validators = validators
}
但是上面的实现要求所有调用者调用 .eraseToAnyValidator()
:
let compoundValidator = CompoundValidator(EmailValidator().eraseToAnyValidator(), RequiredValidator().eraseToAnyValidator())
如果你想减轻调用者的负担,那么你需要在被调用方做一些工作:
struct CompoundValidator<Value> {
let validators: [AnyValidator<Value>]
init<V: Validator>(_ v: V) where V.Value == Value {
validators = [v.eraseToAnyValidator()]
}
init<V1: Validator, V2: Validator>(_ v1: V1, _ v2: V2) where V1.Value == Value, V2.Value == Value {
validators = [v1.eraseToAnyValidator(), v2.eraseToAnyValidator()]
}
init<V1: Validator, V2: Validator, V3: Validator>(_ v1: V1, _ v2: V2, _ v3: V3) where V1.Value == Value, V2.Value == Value, V3.Value == Value {
validators = [v1.eraseToAnyValidator(), v2.eraseToAnyValidator(), v3.eraseToAnyValidator()]
}
,基本上你需要添加尽可能多的初始化程序,因为你想通过代码库传递多少参数。这使您能够以简单的形式调用初始化程序:
let compoundValidator = CompoundValidator(EmailValidator(), RequiredValidator())
我正在尝试在 Swift 中实现通用验证器。验证器只是执行一些验证,即电子邮件验证,并根据值是否有效returns true
或 false
。
protocol Validator {
associatedtype Value
func validate(_ value: Value) -> Bool
}
我还设法实现了这些验证器。如您所见,associatedValue
很有魅力。
struct EmailValidator: Validator {
func validate(_ value: String) -> Bool {
NSPredicate(format: "SELF MATCHES %@", "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}").evaluate(with: value)
}
}
struct RequiredValidator: Validator {
func validate(_ value: String) -> Bool {
!value.isEmpty
}
}
但现在我想要一个复合验证器,它不必符合 Validator
,但它确实需要接受任何 Validator
,其中 associatedValue
是相同。下面我举个例子。
let compoundValidator = CompoundValidator<String>(EmailValidator(), RequiredValidator())
但是,当我尝试实现这个想法时,我 运行 出现了诸如 Cannot specialize non-generic type 'Validator'
或 Cannot convert value of type 'Value' to expected argument type 'Validator.Value'
或 Protocol 'Validator' can only be used as a generic constraint because it has Self or associated type requirements
之类的错误,具体取决于我尝试实现的内容。有没有一种方法可以使用泛型实现这个复合验证器?
struct CompoundValidator<Value> {
let validators: [Validator<Value>]
init(_ validators: Validator<Value>...) {
self.validators = validators
}
func validate(_ value: Value) -> Bool {
validators.reduce(into: true) { partialResult, validator in
partialResult = partialResult && validator.validate(value)
}
}
}
struct CompoundValidator<Value, T: Validator> where T.Value == Value {
let validators: [T]
init(_ validators: T...) {
self.validators = validators
}
func validate(_ value: Value) -> Bool {
validators.reduce(into: true) { partialResult, validator in
partialResult = partialResult && validator.validate(value)
}
}
}
您可以创建一个 AnyValidator
类型,即具体类型。这样您就可以使用它来指定泛型类型参数。
struct AnyValidator<Value>: Validator {
private let validateFunc: (Value) -> Bool
init<T: Validator>(_ validator: T) where T.Value == Value {
validateFunc = validator.validate
}
func validate(_ value: Value) -> Bool {
validateFunc(value)
}
}
extension Validator {
func eraseToAnyValidator() -> AnyValidator<Value> {
.init(self)
}
}
struct CompoundValidator<Value> {
let validators: [AnyValidator<Value>]
init(_ validators: AnyValidator<Value>...) {
self.validators = validators
}
func validate(_ value: Value) -> Bool {
validators.reduce(into: true) { partialResult, validator in
partialResult = partialResult && validator.validate(value)
}
}
}
用法示例:
CompoundValidator(
EmailValidator().eraseToAnyValidator(),
RequiredValidator().eraseToAnyValidator())
您可能希望为少量验证器提供方便的初始化程序:
init<V1, V2>(_ v1: V1, _ v2: V2) where V1: Validator, V2: Validator, V1.Value == Value, V2.Value == Value {
self.init(v1.eraseToAnyValidator(), v2.eraseToAnyValidator())
}
init<V1, V2, V3>(_ v1: V1, _ v2: V2, _ v3: V3) where V1: Validator, V2: Validator, V3: Validator, V1.Value == Value, V2.Value == Value, V3.Value == Value {
self.init(v1.eraseToAnyValidator(), v2.eraseToAnyValidator(), v3.eraseToAnyValidator())
}
允许您省略 .eraseToAnyValidator()
。
这类似于 Combine 的 Publisher
type has a merge
运算符,它最多接受 8 个 个参数,只是为了让您可以合并不同类型的 Publisher
,而无需说 eraseToAnyPublisher
.
让我们从头开始,Validator<Value>
是无效语法,即使 Validator
是具有关联类型的协议。 Swift 还不允许存在性容器(协议引用)声明通用约束,例外是 where
子句。
因此,您需要一个类型橡皮擦才能使用任何类型的泛型:
struct AnyValidator<Value>: Validator {
private let _validate: (Value) -> Bool
init<V: Validator>(_ validator: V) where V.Value == Value {
_validate = validator.validate
}
func validate(_ value: Value) -> Bool {
_validate(value)
}
}
extension Validator {
func eraseToAnyValidator() -> AnyValidator<Value> {
AnyValidator(self)
}
}
,您需要更新定义:
struct CompoundValidator<Value> {
let validators: [AnyValidator<Value>]
init(_ validators: AnyValidator<Value>...) {
self.validators = validators
}
但是上面的实现要求所有调用者调用 .eraseToAnyValidator()
:
let compoundValidator = CompoundValidator(EmailValidator().eraseToAnyValidator(), RequiredValidator().eraseToAnyValidator())
如果你想减轻调用者的负担,那么你需要在被调用方做一些工作:
struct CompoundValidator<Value> {
let validators: [AnyValidator<Value>]
init<V: Validator>(_ v: V) where V.Value == Value {
validators = [v.eraseToAnyValidator()]
}
init<V1: Validator, V2: Validator>(_ v1: V1, _ v2: V2) where V1.Value == Value, V2.Value == Value {
validators = [v1.eraseToAnyValidator(), v2.eraseToAnyValidator()]
}
init<V1: Validator, V2: Validator, V3: Validator>(_ v1: V1, _ v2: V2, _ v3: V3) where V1.Value == Value, V2.Value == Value, V3.Value == Value {
validators = [v1.eraseToAnyValidator(), v2.eraseToAnyValidator(), v3.eraseToAnyValidator()]
}
,基本上你需要添加尽可能多的初始化程序,因为你想通过代码库传递多少参数。这使您能够以简单的形式调用初始化程序:
let compoundValidator = CompoundValidator(EmailValidator(), RequiredValidator())