web-dev-qa-db-fra.com

Appel de l'implémentation par défaut du protocole à partir d'une méthode standard

Je me demande s'il est possible de réaliser une telle chose.
J'ai un terrain de jeu comme celui-ci:

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

Je peux fournir une implémentation par défaut dans extension, mais que se passe-t-il si Bar a besoin de tout ce qui est implémenté par défaut et de choses supplémentaires?
Cela ressemble en quelque sorte à l'appel des méthodes super. dans classes pour répondre à l'exigence d'implémentation de chaque propriété, etc., mais je ne vois aucune possibilité d'obtenir le même résultat avec structs.

64
cojoj

Je ne sais pas si vous cherchez toujours une réponse à cela, mais vous pouvez le faire en supprimant la fonction de la définition du protocole, en convertissant votre objet en Foo, puis en appelant la méthode:

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"

Pour une raison quelconque, cela ne fonctionne que si la fonction n'est pas déclarée dans le cadre du protocole, mais définie dans une extension du protocole. Allez comprendre. Mais, il fonctionne.

73
Aaron Rasmussen

Eh bien, vous pouvez créer un type imbriqué conforme au protocole, l'instancier et appeler la méthode sur celui-ci (peu importe que vous ne puissiez pas accéder aux données de votre type car l'implémentation dans l'extension de protocole ne peut quand même pas y faire référence). Mais ce n'est pas une solution que j'appellerais élégante.

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

Si votre protocole a les exigences associatedType ou Self, la conversion ne fonctionnera pas. Pour contourner ce problème, créez une implémentation par défaut "shadow" pouvant être appelée à la fois par l'implémentation standard par défaut et par le type conforme.

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

Merci pour le post! Si vous mettez la définition de fonction dans le protocole, lorsque l'objet est converti en protocole, il ne voit que la version de l'objet et, puisque vous l'appelez en lui-même, vous obtenez la nouvelle adresse d'Apple ...

J'ai essayé une version comme celle-ci:

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

Cela donne une sortie de:

Structure Version
Extension Version
2
Jim Malak

que pensez-vous de cette façon de résoudre ce problème?

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()
0
Amin Madani