web-dev-qa-db-ja.com

通常のメソッドからプロトコルのデフォルト実装を呼び出す

そのようなことを達成することが可能かどうか疑問に思っています。
このようなプレイグラウンドがあります:

protocol Foo {
    func testPrint()
}

extension Foo {
    func testPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        // Calling self or super go call default implementation
        self.testPrint()
        print("Call from struct")
    }
}


let sth = Bar()
sth.testPrint()

extensionでデフォルトの実装を提供できますが、Barがデフォルトの実装にあるすべてのものと追加のものを必要とする場合はどうなりますか?
それはsuper.classesのメソッドは、すべてのプロパティを実装するなどの要件を満たしますが、structsで同じことを実現する可能性はありません。

72
cojoj

まだこれに対する答えを探しているかどうかはわかりませんが、それを行う方法は、プロトコル定義から関数を削除し、オブジェクトをFooにキャストして、そのメソッドを呼び出すことです:

protocol Foo { 
    // func testPrint() <- comment this out or remove it
}

extension Foo {
    func testPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        print("Call from struct")
        (self as Foo).testPrint() // <- cast to Foo and you'll get the  default
                                  //    function defined in the extension
    }
}

Bar().testPrint()

// Output:    "Call from struct"
//            "Protocol extension call"

何らかの理由で、関数がプロトコルの一部として宣言されていないが、プロトコルの拡張で定義されている場合にのみ機能します。図を移動します。しかし、それは機能します。

81
Aaron Rasmussen

さて、プロトコルに準拠したネストされた型を作成し、インスタンス化して、その型のメソッドを呼び出すことができます(プロトコル拡張機能内の実装がとにかく参照できないため、型のデータにアクセスできないことは問題ではありません)。しかし、それは私がエレガントと呼ぶ解決策ではありません。

struct Bar: Foo {
    func testPrint() {
        // Calling default implementation
        struct Dummy : Foo {}
        let dummy = Dummy()
        dummy.testPrint()
        print("Call from struct")
    }
}
7
Thorsten Karrer

投稿いただきありがとうございます!プロトコルに関数定義を配置すると、オブジェクトがプロトコルとしてキャストされると、オブジェクトのバージョンの関数のみが表示され、内部で呼び出すため、新しいアドレスApple = ...

私はこのようなバージョンを試しました:

import UIKit
protocol MyProc
{
}

protocol MyFuncProc
{
    func myFunc()
}

extension MyProc
{
    func myFunc()
    {
        print("Extension Version")
    }
}

struct MyStruct: MyProc, MyFuncProc
{
    func myFunc()
    {
        print("Structure Version")
        (self as MyProc).myFunc()
    }
}

(MyStruct() as MyFuncProc).myFunc()

これにより、次の出力が得られます。

Structure Version
Extension Version
3
Jim Malak

プロトコルにassociatedTypeまたはSelfの要件がある場合、キャストは機能しません。これを回避するには、通常のデフォルト実装と適合タイプの両方が呼び出すことができる「シャドウ」デフォルト実装を作成します。

protocol Foo { 
    associatedType Bar
}

extension Foo {
    func testPrint() {
        defaultTestPrint()
    }
}

fileprivate extension Foo { // keep this as private as possible
    func defaultTestPrint() {
        // default implementation
    }
}

struct Bar: Foo {
    func testPrint() {
        // specialized implementation
        defaultTestPrint()
    }
}
2
David James

これを修正するそのような方法についてどう思いますか?

protocol Foo {
    func testPrint()
}

extension Foo {
    func testPrint() {
        defaultTestPrint()
    }

    func defaultTestPrint() {
        print("Protocol extension call")
    }
}

struct Bar: Foo {
    func testPrint() {
        // Calling self or super go call default implementation
        defaultTestPrint()
        print("Call from struct")
    }
}


let sth = Bar()
sth.testPrint()
1
Amin Madani