1

* 短縮版 *

クラス (拡張機能) を汎用プロトコル関数に適合させるにはどうすればよいですか?

※ロングバージョン※

これは、ページ分割されたコレクションをサポートするためのデータ構造の小さな部分です。

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> {
    //...
//}

「実際の」ケースを使用したプロトコルの実装は次のとおりです。

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

タイプ「人」はプロトコル「ページ」に準拠していません

プロトコルにはネストされたタイプ 'PageItemType' が必要です

私も明示的にしようとしましたが、別のエラーが発生しました:

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

タイプ「Person」の戻り式を戻りタイプ「PageItemType」に変換できません

だから: *どうすればitemAt、PageItemType typealias の有効な型を関数に返させることができますか?

*ボーナス*

50 報奨金に値するボーナス質問(回答が 1 行より長い場合は、新しい質問を開きます): 最初のコード スニペットを参照するPagedCollection

  • 各 Page 実装には常に Pageable プロトコル オブジェクト タイプの既知の実装があることを前提としています。
  • 宣言を避ける方法はありItemType:Pageableますか? または、少なくともwhere句でそれを強制しますか?
4

2 に答える 2

4

関連する型をジェネリック関数と混同しているようです。

ジェネリック関数を使用すると、呼び出しサイト(つまり、関数を呼び出すとき)で特定のジェネリック プレースホルダーを置き換える型を提供できます。

関連付けられた型を使用すると、プロトコルに準拠する型が独自の型を提供して、プロトコル要件の特定のプレースホルダー型を置き換えることができます。これは、関数の呼び出しサイトではなく、type ごとに行われます。の適合要件を強制したい場合はassociatedtype、その宣言 (つまりassociatedtype PageItemType : Pageable) で直接行う必要があります。

私があなたの要件を正しく理解していれば、itemAt(index:)関数は非ジェネリックである必要があります (そうでなければ、associatedtypeプロトコルの は完全に冗長になります)。Page返される型は、関数の呼び出し元ではなく、 に準拠する型の実装によって定義されます。たとえば、関連付けられた型が a である必要があることPeopleをクラスで定義し、それが返される必要があるとします。PageItemTypePersonitemAt(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
    }
}

PagedCollectiona –の実装に関しては、プロトコルのPageItemType関連付けられた型が に準拠しているため、ジェネリック パラメータは必要ないと思います。これには、指定されたジェネリック パラメーターの関連付けられた型を介して簡単にアクセスできます。PagePageableItemTypePageType

例として:

class PagedCollection<PageType:Page> {

    // PageType's associatedtype, which will conform to Pageable.
    // In the case of the PageType generic parameter being People,
    // PageType.PageItemType would be of type Person
    var foo : PageType.PageItemType

    init(foo: PageType.PageItemType) {
        self.foo = foo
    }
}

// p.foo is of type Person, and is guarenteed to conform to Pageable
let p = PagedCollection<People>(foo: Person())
于 2016-09-16T20:47:44.790 に答える