繁体   English   中英

无法在Swift的另一个协议中将协议用作关联类型

[英]Unable to use protocol as associatedtype in another protocol in Swift

我有一个协议Address ,它继承自另一个协议Validator ,并且Address满足扩展名中Validator要求。

还有另一种协议, FromRepresentable ,其具有associatedTypeValueWrapper这应该是)要求Validator

现在,如果我尝试使用Address作为associatedType ,那么它将无法编译。 它说,

推断的类型“地址”(通过匹配要求“ 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限制为特定的(非@objc )协议,就必须使用具体的类型来满足该要求。

这是因为协议不符合自身 ,因此,意味着您不能使用Address来满足协议的与Validator一致的类型的关联类型要求,因为Address 不是Validator的类型。

如我在此处的答案所示 ,请考虑以下反例:

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
}

最简单的解决方案是ValueWrapper关联类型上的Validator协议约束,从而允许您在method参数中使用抽象类型。

protocol FormRepresentable {
    associatedtype ValueWrapper
    func valueForDetail(valueWrapper: ValueWrapper) -> String
}

enum AddressFrom : Int, FormRepresentable {

    // ...

    func valueForDetail(valueWrapper: Address) -> String {
        // ...
    }
}

如果您需要关联的类型约束,并且每个AddressFrom实例仅期望将Address的单个具体实现作为输入–您可以使用泛型,以便使用要在方法中使用的给定具体地址类型来初始化AddressFrom

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

但是,如果同时需要关联的类型约束每个AddressFrom实例必须能够处理任何类型的Address的输入,则必须使用类型擦除来将任意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))

您遇到了几个问题:

首先,您实际上并未声明Address实现了Validator

//Address inherits Validator
protocol Address : Validator {
    var addressLine1: String {get set}
    var city: String {get set}
    var country: String {get set}
}

而且您没有声明ValueWrapper的关联类型:

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
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM