नियमित विधि से प्रोटोकॉल डिफ़ॉल्ट कार्यान्वयन का आह्वान


83

मैं सोच रहा था कि क्या ऐसा हासिल करना संभव है।
मैं इस तरह एक खेल का मैदान है:

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.मेथड के समान है, जिसमें classहर प्रॉपर्टी आदि को लागू करने की आवश्यकता को पूरा किया जाता है, लेकिन मुझे इसके साथ ही हासिल करने की कोई संभावना नहीं दिखती है structs


मैं उपयोग करूंगा Foo.testPrint(self)()- समस्या यह है कि यह एक विभाजन दोष के कारण विफल रहता है (दोनों 7.0 जीएम और 7.1 बीटा पर परीक्षण किया गया)
एंटोनियो

1
यह एक अजीब निर्माण है जिसे आपने 😯
cojoj

4
हर उदाहरण विधि एक स्थिर करी विधि है जो अपने पहले पैरामीटर के रूप में एक उदाहरण लेती है
एंटोनियो

हालाँकि मैंने एक्सटेंशन को हटाने की कोशिश की, और यह समान विभाजन त्रुटि को फेंकता है। संभवतः वह प्रोटोकॉल के साथ काम करने वाला नहीं है
एंटोनियो

हम्म, शर्म की बात है कि मुझे खुद को कोड में दोहराना
पड़ा है

जवाबों:


90

मैं नहीं जानता कि क्या आप अभी भी इसका उत्तर ढूंढ रहे हैं, लेकिन ऐसा करने का तरीका प्रोटोकॉल परिभाषा से फ़ंक्शन को निकालना है, अपनी वस्तु को डालना 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"

किसी कारण से यह केवल तभी काम करता है जब फ़ंक्शन को प्रोटोकॉल के भाग के रूप में घोषित नहीं किया जाता है, लेकिन प्रोटोकॉल के विस्तार में परिभाषित किया गया है। जाओ पता लगाओ। लेकिन यह काम करता है।


1
हे भगवान! मैं विश्वास नहीं कर सकता कि वे आपको प्रोटोकॉल से बाहर निकालने के लिए मजबूर करते हैं! अच्छा जवाब, धन्यवाद!
स्काईवॉकर

5
यह मेरे लिए एक बग की तरह लग रहा है, यह केवल एक ही उदाहरण कास्टिंग द्वारा अलग-अलग विधि कार्यान्वयन प्राप्त करने के लिए संभव नहीं होना चाहिए।
MANIAK_dobrii

15
यह वास्तव में प्रोटोकॉल + विस्तार के शब्दार्थ को काफी बदल देता है। यदि आप प्रोटोकॉल से घोषणा को छोड़ देते हैं, तो आप फ़ंक्शन को उस प्रकार पर कॉल करते समय स्थिर प्रेषण प्राप्त करेंगे जो प्रोटोकॉल के अनुरूप है - यही कारण है कि आप एक्सटेंशन से कास्ट और कार्यान्वयन प्राप्त कर सकते हैं। यदि आप प्रोटोकॉल में घोषणा को जोड़ते हैं, तो फ़ंक्शन को आपका कॉल गतिशील रूप से भेजा जाएगा।
थोरस्टेन कर्रर

2
यह तभी काम करता है जब Fooप्रोटोकॉल किसी अन्य प्रोटोकॉल से विरासत में नहीं आता है।
अयना

4
यह एक अनंत लूप के लिए नेतृत्व नहीं करता है?
stan liu

9

ठीक है, आप प्रोटोकॉल के अनुरूप एक नेस्टेड प्रकार बना सकते हैं, इसे इंस्टेंट कर सकते हैं, और उस पर विधि को कॉल कर सकते हैं (इससे कोई फर्क नहीं पड़ता कि आप अपने प्रकार के डेटा तक नहीं पहुंच सकते क्योंकि प्रोटोकॉल एक्सटेंशन के अंदर कार्यान्वयन वैसे भी इसका संदर्भ नहीं दे सकता है)। लेकिन यह एक समाधान नहीं है जिसे मैं सुरुचिपूर्ण कहूंगा।

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

1
ऐसा लगता है कि यह अभी एकमात्र संभावना है (Apple द्वारा पुष्टि की गई है) ... मैं इस के लिए एक फीचर रडार दर्ज करूंगा क्योंकि यह उपयोगी हो सकता है
cojoj

4

पोस्ट के लिए धन्यवाद! यदि आप फ़ंक्शन की परिभाषा को प्रोटोकॉल में रखते हैं, तो जब ऑब्जेक्ट को प्रोटोकॉल के रूप में डाला जाता है, तो यह केवल फ़ंक्शन के ऑब्जेक्ट के संस्करण को देखता है और जब से आप इसे अपने अंदर बुला रहे हैं, तब आपको 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

मामले में अपने प्रोटोकॉल है 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()
    }
}

आप से अधिक सहमत नहीं हो सकते। defaultXX()अन्य उत्तरों की तुलना में बहुत अधिक अभिव्यंजक और पठनीय है।
डॉनसॉन्ग

और मुझे लगता है कि अमीन मदनी ने आपके उत्तर में सुधार किया।
डॉनसॉन्ग

2

आप इसे ठीक करने के ऐसे तरीके के बारे में क्या सोचते हैं?

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

क्या यह एक जवाब है?
एह फाम

@AnhPham इसे डिफ़ॉल्ट कार्यक्षमता प्रदर्शन करने के लिए सिर्फ एक और तरीका है
अमीन मदनी

क्लीवर और आसान समाधान
स्टीफन जानुआर

सबसे स्पष्ट और लचीला जवाब।
डॉनसॉन्ग

2

मैं इसके लिए एक समाधान लेकर आया हूं।

मुद्दा

जब आप एक्सटेंशन में डिफ़ॉल्ट कार्यान्वयन करते हैं, जब आप प्रोटोकॉल को किसी अन्य वर्ग / संरचना पर लागू करते हैं, तो आप इस डिफ़ॉल्ट कार्यान्वयन को खो देते हैं यदि आप विधि को लागू करते हैं। यह डिजाइन द्वारा है, यह है कि प्रोटोकॉल कैसे काम करता है

उपाय

  • अपने प्रोटोकॉल का एक डिफ़ॉल्ट कार्यान्वयन बनाएं और इसे अपने प्रोटोकॉल की एक संपत्ति बनाएं।
  • फिर जब आप इस प्रोटोकॉल को एक कक्षा में लागू करते हैं, तो एक गेट्टर के साथ अपना डिफ़ॉल्ट कार्यान्वयन प्रदान करें
  • आवश्यकता होने पर डिफ़ॉल्ट कार्यान्वयन को कॉल करें।

उदाहरण


protocol Foo {
    var defaultImplementation: DefaultImpl? { get }
    func testPrint()
}

extension Foo {
    // Add default implementation
    var defaultImplementation: DefaultImpl? {
        get {
            return nil
        }
    }
}

struct DefaultImpl: Foo {
    func testPrint() {
        print("Foo")
    }
}


extension Foo {
    
    func testPrint() {
        defaultImplementation?.testPrint()
    }
}

struct Bar: Foo {
    
    var defaultImplementation: DefaultImpl? {
        get { return DefaultImpl() }
    }
    func testPrint() {
        if someCondition {
            defaultImplementation?.testPrint() // Prints "Foo"
        }
    }
}

struct Baz: Foo {
    func testPrint() {
        print("Baz")
    }
}


let bar = Bar()
bar.testPrint() // prints "Foo"

let baz = Baz()
baz.testPrint() // prints "Baz"


कमियां

आप इस प्रोटोकॉल को लागू करने वाले संरचना / वर्ग में आवश्यक कार्यान्वयन त्रुटि खो देते हैं।


1
कृपया स्पष्ट करें कि आपका कोड क्यों काम करता है। यह उन लोगों की मदद करता है जो आपके उत्तर पर जाते हैं, इससे सीखते हैं।
एलेक्सएच

हाँ यह एक अच्छा कार्यान्वयन है!
juxhin bleta
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.