当前位置: 首页 > 面试题库 >

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

赵正雅
2023-03-14
问题内容

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

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

现在,如果我尝试使用Addressas 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
}

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

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


 类似资料:
  • 协议(Protocols)为方法,属性和其他需求功能提供了蓝图。 它为方法或属性骨架而不是实现。 通过定义类,函数和枚举,可以进一步完成方法和属性的实现。 协议的一致性满足了协议要求的方法或属性。 语法 协议也遵循与类,结构和枚举类似的语法 - 协议在类,结构或枚举类型名称之后声明。 单个和多个协议声明也是可以的。 如果定义了多个协议,则必须用逗号分隔。 当要为超类定义协议时,协议名称应使用逗号跟

  • 协议规定了用来实现某一特定功能所必需的方法和属性。 任意能够满足协议要求的类型被称为遵循(conform)这个协议。 类,结构体或枚举类型都可以遵循协议,并提供具体实现来完成协议定义的方法和功能。 语法 协议的语法格式如下: protocol SomeProtocol { // 协议内容 } 要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循

  • 问题内容: 在下面的代码中,我想测试是否为。如果是的话,我想作为。你怎么做到这一点?如果不使用强制转换,则使用其他技术。 那里的最后一行不会编译。错误是: 协议“ SpecialController”仅具有通用要求或关联类型要求,因此只能用作通用约束。 问题答案: 不幸的是,Swift当前不支持将具有关联类型的协议用作实际类型。但是,从技术上讲这对于编译器是可行的。并且很可能在该语言的未来版本中实

  • 点击此处可下载并查看多方主体协议请将多方主体协议下载打印并按要求填写,并将填好的协议快递至: 地址:北京市海淀区东北旺西路8号院尚东数字山谷B区1号楼 收件人:企业级事业部 联系电话:010-83041496具体填写要求如下: 甲方处填写客户已经注册或开户使用的公司名称 甲方关联公司名录处填写贵公司将要关联的公司名称 盖章处需加盖甲方及其关联公司的公章 乙方名称为必填,(名称:滴滴出行科技有限公司

  • 点击此处可下载并查看多方主体协议请将多方主体协议下载打印并按要求填写,并将填好的协议快递至: 地址:北京市海淀区东北旺西路8号院尚东数字山谷B区1号楼 收件人:企业级事业部,联系方式:010-83041496填写要求: 甲方处填写客户已经注册或开户使用的公司名称 甲方关联公司名录处填写贵公司将要关联的公司名称(可以有多个) 盖章处需加盖甲方及其关联公司的公章 乙方名称为必填,(名称:滴滴出行科技有

  • 问题内容: protocol BasePresenterProtocol : class {} protocol DashboardPresenterProtocol : BasePresenterProtocol {} 在上面的代码中,我在下一行收到错误 ,但未确认协议,但我已在中声明。虽然我已经宣布 为什么会出现此错误?请让我知道我在这段代码中做错了什么。 问题答案: 您不能使用type 属性