将类扩展符合通用协议函数

时间:2016-09-16 19:43:03

标签: swift generics swift-protocols associated-types generic-function

*短版*

如何使类(扩展名)符合通用协议函数?

*长版*

这是支持分页集合的数据结构的一小部分,

protocol Pageable { 
     //an object whose can be in a collection
}

protocol Page{ //a page of a collection that can be paginated

    associatedtype PageItemType

    func itemAt<PageItemType:Pageable>(index: Int) -> PageItemType  
}

//Bonus question
//class PagedCollection<PageType:Page, ItemType:Pageable> {
    //...
//}

这是使用&#34; real&#34;的协议的实现。情况下:

class Person : Pageable{}

class People {
    var people: [Person]?
}

//Mark: - Page

extension People: Page{ /*** error 1 ***/

    typealias PageItemType = Person

    func itemAt(index: Int) -> Person{
        let person : Person = self.people![index]
        return person
    }
}

获取以下错误(1):

  

键入&#39;人物&#39;不符合协议&#39; Page&#39;

     
    

协议需要嵌套类型&#39; PageItemType&#39;

  

我也试过让它明确但我只是有一个不同的错误:

//Mark: - Page

extension People: Page{

    typealias PageItemType = Person

    func itemAt<PageItemType:Pageable>(index: Int) -> PageItemType{
        let person : Person = self.people![index]
        return person /*** error 2 ***/
    }
}

获取以下错误(2):

  

无法转换类型&#39; Person&#39;的返回表达式返回类型&#39; PageItemType&#39;

所以:*我如何让itemAt函数返回PageItemType类型的有效类型?

*奖金*

奖金问题值50分(如果答案长于一行我会打开一个新问题): 请参阅第一个代码段PagedCollection

  • 鉴于每个Page实现始终具有Pageable protocol objet类型的已知实现
  • 有没有办法避免声明ItemType:Pageable?或者至少使用where条款强制执行它?

2 个答案:

答案 0 :(得分:3)

您似乎正在将相关类型与通用函数混淆。

通用函数允许您提供一种类型来替换调用站点上的给定通用占位符(即,当您调用该函数时)。

关联类型允许符合协议的类型提供自己的类型来替换协议要求中的给定占位符类型。这是每种类型,而不是在任何函数的调用站点。如果您希望强制执行struct Intermediary { int data; LinkedList<int> sublist; }; LinkedList<Intermediary> list; 的一致性要求,则应直接对其声明(即std::pair)执行此操作。

如果我正确理解您的要求,您的associatedtype功能应该是非通用的(否则协议中的associatedtype PageItemType : Pageable完全是多余的)。它返回的类型由符合itemAt(index:)的类型的实现定义,而不是函数的调用者。例如,您的associatedtype类定义Page相关类型应为People - 这就是PageItemType应返回的内容。

Person

关于itemAt(index:)的实施 - 由于protocol Pageable {/* ... */} protocol Page { // any conforming type to Page will need to define a // concrete type for PageItemType, that conforms to Pageable associatedtype PageItemType : Pageable // returns the type that the implementation of the protocol defines // to be PageItemType (it is merely a placeholder in the protocol decleration) func itemAt(index: Int) -> PageItemType } class Person : Pageable {/* ... */} class People { var people: [Person]? } extension People : Page { // explicitly satisfies the Page associatedtype requirement. // this can be done implicitly be the itemAt(index:) method, // so could be deleted (and annotate the return type of itemAt(index:) as Person) typealias PageItemType = Person // the itemAt(index:) method on People now returns a Person func itemAt(index: Int) -> PageItemType { // I would advise against force unwrapping here. // Your people array most likely should be non-optional, // with an initial value of an empty array // (do you really need to distinguish between an empty array and no array?) let person = self.people![index] return person } } 协议中的PagedCollection相关类型符合PageItemType,我认为不需要{{1}通用参数。这可以通过给定Page通用参数的关联类型来访问。

举个例子:

Pageable

答案 1 :(得分:1)

这只是我与chat与Hamish进行的格式化讨论。

我在这里写它,因为聊天室可以存档,有时更直接的QA可以传达不同的信息

我:

protocol Provider { 
    associatedtype Input 
    associatedtype Output 
    func value(forKey _key: Input) -> Output{} 
}

vs。

protocol Provider{ 
    func value<Input, Output>(forKey _key: Input) -> Output{} 
}

Hamish:本质上,区别在于满足占位符的位置–在类型级别满足关联的类型,而在函数的调用位置满足函数上的通用占位符。

:我了解这种差异。但是由于这种差异,是否有任何副产品:D

Hamish:是什么意思?它们表达了不同的内容-前者是协议,其中一致类型仅处理一种特定类型的输入和输出,后者是协议,其中一致类型可以处理任何给定的输入和输出类型。

我: 前者是一种协议,其中一致类型仅处理一种特定类型的输入和输出。您是什么意思? typeAlias不能成为我喜欢的东西吗?

Hamish:可以,但是每种类型只能满足一次-一旦我定义

struct S : Provider { 
   func value(forKey: String) -> Int 
}

S现在只能处理String输入和Int输出。 而对于后者,我将定义

struct S : Provider { 
    func value<Input, Value>(forKey _key: Input) -> Output{} 
} 

现在S必须能够处理任何输入和输出类型。