无法在Swift中将协议用作其他协议中的关联types
我有一个协议Address
,它从另一个协议inheritance, Validator
和Address
满足扩展中的Validator
要求。
还有另一种协议, FromRepresentable
,它有一个associatedType
( ValueWrapper
)要求,应该是Validator
。
现在,如果我尝试使用Address
作为associatedType
,那么它不会编译。 它说,
推断types“地址”(通过匹配要求'valueForDetail')是无效的:不符合'Validator'。
这个用法是非法的吗? 我们不应该使用Address
来代替Validator
,因为所有的Addresses
都是Validator
。
下面是我正在尝试的代码片段。
enum ValidationResult { case Success case Failure(String) } protocol Validator { func validate() -> ValidationResult } //Address inherits Validator protocol Address: Validator { var addressLine1: String {get set} var city: String {get set} var country: String {get set} } ////Fulfill Validator protocol requirements in extension extension Address { func validate() -> ValidationResult { if addressLine1.isEmpty { return .Failure("Address can not be empty") } return .Success } } protocol FormRepresentable { associatedtype ValueWrapper: Validator func valueForDetail(valueWrapper: ValueWrapper) -> String } // Shipping Address conforming to Address protocol. // It should also implicitly conform to Validator since // Address inherits from Validator? struct ShippingAddress: Address { var addressLine1 = "CA" var city = "HYD" var country = "India" } // While compiling, it says: // Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform // to 'Validator'. // But Address confroms to Validator. enum AddressFrom: Int, FormRepresentable { case Address1 case City case Country func valueForDetail(valueWrapper: Address) -> String { switch self { case .Address1: return valueWrapper.addressLine1 case .City: return valueWrapper.city case .Country: return valueWrapper.country } } }
更新:提交了一个错误。
David已经避免了这个问题,一旦你将一个协议的associatedtype
约束到一个特定的协议,你必须使用一个具体的types来满足
需求。
这是因为协议不符合自己 – 因此意味着您不能使用Address
来满足符合Validator
的协议的关联types要求,因为Address
不是符合Validator
的types。
正如我在这里的答案中所表明的那样 ,考虑下面的例子:
protocol Validator { init() } protocol Address : Validator {} protocol FormRepresentable { associatedtype ValueWrapper: Validator } extension FormRepresentable { static func foo() { // if ValueWrapper were allowed to be an Address or Validator, // what instance should we be constructing here? // we cannot create an instance of a protocol. print(ValueWrapper.init()) } } // therefore, we cannot say: enum AddressFrom : FormRepresentable { typealias ValueWrapper = Address }
最简单的解决scheme是在你的ValueWrapper
关联types上ValueWrapper
Validator
协议约束,允许你在方法参数中使用一个抽象types。
protocol FormRepresentable { associatedtype ValueWrapper func valueForDetail(valueWrapper: ValueWrapper) -> String }
enum AddressFrom : Int, FormRepresentable { // ... func valueForDetail(valueWrapper: Address) -> String { // ... } }
如果您需要关联的types约束,并且每个AddressFrom
实例只需要Address
的一个具体实现作为input – 您可以使用generics为了使AddressFrom
初始化为在方法中使用给定的具体types的地址。
protocol FormRepresentable { associatedtype ValueWrapper : Validator func valueForDetail(valueWrapper: ValueWrapper) -> String }
enum AddressFrom<T : Address> : Int, FormRepresentable { // ... func valueForDetail(valueWrapper: T) -> String { // ... } }
// replace ShippingAddress with whatever concrete type you want AddressFrom to use let addressFrom = AddressFrom<ShippingAddress>.Address1
但是,如果您需要关联的types约束, 并且每个AddressFrom
实例必须能够处理任何types的Address
的input – 您将使用types擦除来包装具体types中的任意Address
。
protocol FormRepresentable { associatedtype ValueWrapper : Validator func valueForDetail(valueWrapper: ValueWrapper) -> String }
struct AnyAddress : Address { private var _base: Address var addressLine1: String { get {return _base.addressLine1} set {_base.addressLine1 = newValue} } var country: String { get {return _base.country} set {_base.country = newValue} } var city: String { get {return _base.city} set {_base.city = newValue} } init(_ base: Address) { _base = base } }
enum AddressFrom : Int, FormRepresentable { // ... func valueForDetail(valueWrapper: AnyAddress) -> String { // ... } }
let addressFrom = AddressFrom.Address1 let address = ShippingAddress(addressLine1: "", city: "", country: "") addressFrom.valueForDetail(AnyAddress(address))
你有几个问题:
首先,你并没有实际声明该地址实现了validation器
//Address inherits Validator protocol Address : Validator { var addressLine1: String {get set} var city: String {get set} var country: String {get set} }
而且您不要声明ValueWrapper的关联types:
typealias ValueWrapper = ShippingAddress
而你似乎实际上想要AddressFrom.valueForDetail采取ShippingAddress
:
func valueForDetail(valueWrapper: ShippingAddress) -> String { switch self { case .Address1: return valueWrapper.addressLine1 case .City: return valueWrapper.city case .Country: return valueWrapper.country } }
总的来说,它看起来像:
enum ValidationResult { case Success case Failure(String) } protocol Validator { func validate() -> ValidationResult } //Address inherits Validator protocol Address : Validator { var addressLine1: String {get set} var city: String {get set} var country: String {get set} } ////Fulfill Validator protocol requirements in extension extension Address { func validate() -> ValidationResult { if addressLine1.isEmpty { return .Failure("Address can not be empty") } return .Success } } protocol FormRepresentable { associatedtype ValueWrapper: Validator func valueForDetail(valueWrapper: ValueWrapper) -> String } // Shipping Address conforming to Address protocol. // It should also implicity conform to Validator since // Address inherits from Validator? struct ShippingAddress: Address { var addressLine1 = "CA" var city = "HYD" var country = "India" } // While compiling, it says: // Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform // to 'Validator'. // But Address confroms to Validator. enum AddressFrom: Int, FormRepresentable { case Address1 case City case Country // define associated type for FormRepresentable typealias ValueWrapper = ShippingAddress func valueForDetail(valueWrapper: ShippingAddress) -> String { switch self { case .Address1: return valueWrapper.addressLine1 case .City: return valueWrapper.city case .Country: return valueWrapper.country } } }