简体   繁体   English

难以理解Swift协议扩展中的“关联类型”

[英]Difficulty understanding `Associated Types` in Swift protocol extensions

I'm struggling to understand protocols and protocol extensions in swift. 我正在努力快速地了解协议和协议扩展。

I'm wanting to define a series of protocols that can be applied to a class, along with a set of protocol extensions to provide default implementations. 我想定义一系列可应用于类的协议,以及一组协议扩展以提供默认的实现。 Example code: 示例代码:

// MARK: - Protocols & Protocol Extensions
protocol OutputItem {
    typealias ResultType
    func rawValue() -> ResultType
    // other requirements ...
}

protocol StringOutputItem : OutputItem {}
extension StringOutputItem {
    typealias ResultType = String
    override func rawValue() -> Self.ResultType {
        return "string ouput"
    }
}

protocol IntOutputItem: OutputItem {}
extension IntOutputItem {
    typealias ResultType = Int
    override func rawValue() -> Self.ResultType {
        return 123
    }
}

The above override functions for rawValue() in the extensions give an error Ambiguous type name 'ResultType' in 'Self' . 扩展中rawValue()的上述重写函数Ambiguous type name 'ResultType' in 'Self'给出了错误的Ambiguous type name 'ResultType' in 'Self' If I remove Self , from Self.ResultType , I get the error 'ResultType' is ambiguous for type lookup in this context . 如果我从Self.ResultType删除Self'ResultType' is ambiguous for type lookup in this context ,我将得到错误'ResultType' is ambiguous for type lookup in this context

How do I signal to the protocol extension which type to use for ResultType ? 我如何向协议扩展发出信号,以将哪种类型用于ResultType

My aim is to be able to apply the protocols and their extensions to a class as follows: 我的目标是能够将协议及其扩展应用于以下类:

// MARK: - Base Class
class DataItem {
    // Some base class methods
    func randomMethod() -> String {
        return "some random base class method"
    }
}

// MARK: - Subclasses
class StringItem : DataItem, StringOutputItem {
    // Some subclass methods
}

class AnotherStringItem : DataItem, StringOutputItem {
    // Some subclass methods
}

class IntItem : DataItem, IntOutputItem {
    // Some subclass methods
}

So that: 以便:

let item1 = StringItem()
print(item1.rawValue())         // should give "string output"

let item2 = AnotherStringItem()
print(item2.rawValue())         // should give "string output"

let item3 = IntItem()
print(item3.rawValue())         // should give 123

If I'm completely off base with how protocol extensions work to provide default implementations, I'm open ideas of how to achieve the same outcome. 如果我对协议扩展如何提供默认实现完全不满意,那么我对如何实现相同结果持开放态度。

The Swift compiler infers the type of ResultType by the type signatures of the implemented protocol methods. Swift编译器通过已实现的协议方法的类型签名来推断ResultType的类型。 For example, in the following declaration of StringOutputItem , the compiler knows that StringOutputItem 's ResultType is of type String , even without an explicit declaration: 例如,在以下StringOutputItem声明中,编译器知道StringOutputItemResultType的类型为String ,即使没有显式声明也是如此:

protocol StringOutputItem: OutputItem {}

extension StringOutputItem {
    func rawValue() -> String {
        return "string output"
    }
}

class StringItem : DataItem, StringOutputItem {}

let item = StringItem()
print(item.rawValue()) // prints "string output"

We can explicitly declare the ResultType in StringOutputItem , which will just ensure that StringOutputItem conforms to the OutputItem protocol AND implements it with the right type. 我们可以在ResultType中显式声明StringOutputItem ,这将仅确保StringOutputItem符合OutputItem协议并以正确的类型实现它。

To illustrate the type inference of the associated type, let's say that OutputItem specifies another method as part of its protocol. 为了说明关联类型的类型推断,假设OutputItem指定另一种方法作为其协议的一部分。 If we provide a default implementation whereby the types do not match, the compiler will throw an error indicating that the implementing type does not conform to the protocol. 如果我们提供默认的实现,其中类型不匹配,则编译器将抛出错误,指示实现的类型不符合协议。

protocol OutputItem {
    typealias ResultType
    func rawValue() -> ResultType
    func printValue(r: ResultType)
}

protocol StringOutputItem: OutputItem {}

extension StringOutputItem {
    func rawValue() -> String {
        return "string output"
    }
    func printValue(r: Int) {  // Should be String
        ...
    }
}

struct Test: StringOutputItem {} // Error: Type 'Test' does not conform to protocol 'OutputItem'

By explicitly declaring typealias ResultType = String in StringOutputItem , we are making sure that the correct type is used when implementing the protocol's methods. 通过在StringOutputItem显式声明typealias ResultType = String ,我们可以确保在实现协议的方法时使用正确的类型。

protocol OutputItem {
    typealias ResultType
    func rawValue() -> ResultType
    func printValue(r: ResultType)
}

protocol StringOutputItem: OutputItem {}

extension StringOutputItem {
    typealias ResultType = String // without this typealias declaration, the program WILL compile since ResultType is inferred to be of type Int
    func rawValue() -> Int {
        return 123
    }
    func printValue(r: Int) {  
        ...
    }
}

struct Test: StringOutputItem {} // Error: Type 'Test' does not conform to protocol 'OutputItem'

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

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