Swift 带有关联类型的协议(类型查找不明确)

Swift protocol with associatedtype (ambiguous for type lookup)

我需要在协议中创建通用函数,在扩展中使用默认实现。它的功能应该与 item 一起工作,如 enum:RawRepresentable where RawValue == String always。我试过了

protocol RequiresEnum: class {
    associatedtype SectionIdentifierEnum: RawRepresentable // how this add restriction to RawValue == String

    func test<T: SectionIdentifierEnum>(identifier: T) where T.RawValue == String
}

enum RequiresEnumDefault: String {
    case `default`
}

extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum.RawValue == String {

    typealias SectionIdentifierEnum = RequiresEnumDefault

    func test<T: SectionIdentifierEnum>(identifier: T) where T.RawValue == String {
        print(T.rawValue)
    }

}

但我有错误

  • 'SectionIdentifierEnum' is ambiguous for type lookup in this context
  • 'RawValue' is not a member type of 'T'

任何解决方案

通常,在协议上下文中涵盖泛型时,泛型类型持有者被视为可由协议的 associatedtype 表示。在您的示例中,这将是 SectionIdentifierEnum,它充当受约束的 type 的占位符。但是,SectionIdenfierEnum 本身不是协议,因此不能将其用作泛型方法中的类型约束。但是,您 可以 将其用作 test(...) 方法中的类型。


Swift3.1

现在,目前 (Swift 3.1),您不能向 associatedtype 添加复杂的类型约束。但是,您可以提供仅适用于 Self 派生自 UIViewController 并通过将 SectionIdentifierEnum 类型设置为具体来实现 RequiresEnum 协议的情况的默认实现RequiresEnumDefault 类型。后者将确定关联的 RawValue 是此默认实现的 String,因为具体 RequiresEnumDefault 类型的 RawValueString.

例如:

// Swift 3.1
// ---------
// Types that implement this protocol mustn't necessarily use a
// `SectionIdentifierEnum` type where `SectionIdentifierEnum.RawValue` 
// is constrained to equal `String`. 
protocol RequiresEnum: class {
    associatedtype SectionIdentifierEnum: RawRepresentable

    func test(identifier: SectionIdentifierEnum)
}

enum RequiresEnumDefault: String {
    case `default`
}

// This extension, however, is only available for types that use
// `RequiresEnumDefault ` as the concrete type of `SectionIdentifierEnum`
// (in which case `SectionIdentifierEnum.RawValue` is `String`).
extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum == RequiresEnumDefault {

    func test(identifier: SectionIdentifierEnum) {
        print(identifier.rawValue)
    }
}

// Example usage.
class MyViewController : UIViewController, RequiresEnum {
    typealias SectionIdentifierEnum = RequiresEnumDefault
    // ...
}

let foo = MyViewController()
foo.test(identifier: RequiresEnumDefault.default) 
  // prints "default" (using extension:s default implementation)

以上,test(...) 的默认实现仅在 SectionIdentifierEnum 等于具体类型 RequireEnumDefault 时可用(并且 Self 派生自 UIViewController .. .).相反,如果您希望它仅在 SectionIdentifierEnum 任何 String 类型为 RawValue 的枚举时可用,您可以相应地修改扩展的类型约束:

protocol RequiresEnum: class {
    associatedtype SectionIdentifierEnum: RawRepresentable

    func test(identifier: SectionIdentifierEnum)
}

enum RequiresEnumDefault: String {
    case `default`
}

extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum.RawValue == String {

    func test(identifier: SectionIdentifierEnum) {
        print(identifier.rawValue)
    }
}


// Example usage.
enum EnumWithStringRawValue: String {
    case foo
}

class MyViewController : UIViewController, RequiresEnum {
    typealias SectionIdentifierEnum = EnumWithStringRawValue
    // ...
}

let foo = MyViewController()
foo.test(identifier: EnumWithStringRawValue.foo)
    // prints "foo" (using extension:s default implementation)

一旦 Swift 4 发布,您将能够向 associatedtype:s 添加更复杂的约束,根据 Swift 进化提案的实施:

这样的话上面可以修改成:

// Swift 4
// -------
// Here, all types that implement this protocol must use a
// `SectionIdentifierEnum` type where `SectionIdentifierEnum.RawValue` 
// is equal to `String`. 
protocol RequiresEnum: class {
    associatedtype SectionIdentifierEnum: RawRepresentable 
        where SectionIdentifierEnum.RawValue == String

    func test(identifier: SectionIdentifierEnum)
}

enum RequiresEnumDefault: String {
    case `default`
}

// For the specific case where `SectionIdentifierEnum` equals
// `RequiresEnumDefault` (and where `Self` derives from `UIViewController`), 
// this default implementation is readily available.
extension RequiresEnum where Self: UIViewController, SectionIdentifierEnum == RequiresEnumDefault {

    func test(identifier: SectionIdentifierEnum) {
        print(identifier.rawValue)
    }

}

// Example usage.
class MyViewController : UIViewController, RequiresEnum {
    typealias SectionIdentifierEnum = RequiresEnumDefault
    // ...
}

let foo = MyViewController()
foo.test(identifier: RequiresEnumDefault.default) 
  // prints "default" (using extension:s default implementation)

同样修改对 test(...) 默认实现的约束,不仅针对 SectionIdentifierEnum 等于 RequiresEnumDefault 的情况(而且针对任何枚举:在这种情况下我们知道这样的枚举将由于协议定义中对 associatedtype 的限制,总是有 RawValue String

protocol RequiresEnum: class {
    associatedtype SectionIdentifierEnum: RawRepresentable 
        where SectionIdentifierEnum.RawValue == String

    func test(identifier: SectionIdentifierEnum)
}

enum RequiresEnumDefault: String {
    case `default`
}

// From the constraint on the `RawValue` of `SectionIdentifierEnum`
// above, we know that `RawValue` equals `String`.
extension RequiresEnum where Self: UIViewController {

    func test(identifier: SectionIdentifierEnum) {
        print(identifier.rawValue)
    }
}

// Example usage.
enum EnumWithStringRawValue: String {
    case foo
}
class MyViewController : UIViewController, RequiresEnum {
    typealias SectionIdentifierEnum = EnumWithStringRawValue
    // ...
}

let foo = MyViewController()
foo.test(identifier: EnumWithStringRawValue.foo) 
  // prints "foo" (using extension:s default implementation)