स्पष्ट रूप से एक सामान्य कार्य का विशेषज्ञ नहीं हो सकता


92

मेरे पास निम्नलिखित कोड के साथ समस्या है:

func generic1<T>(name : String){
}

func generic2<T>(name : String){
     generic1<T>(name)
}

कंपाइलर त्रुटि के लिए जेनेरिक 1 (नाम) परिणाम "एक जेनेरिक फ़ंक्शन को स्पष्ट रूप से विशेषज्ञ नहीं कर सकता"

क्या इस त्रुटि से बचने का कोई तरीका है? मैं जेनेरिक 1 फ़ंक्शन के हस्ताक्षर नहीं बदल सकता, इसलिए यह होना चाहिए (स्ट्रिंग) -> शून्य


2
जब यह संदर्भ के लिए अनुमानित नहीं हो सकता है तो यहां सामान्य प्रकार का उपयोग करने का बिंदु क्या है? यदि जेनेरिक प्रकार का उपयोग केवल आंतरिक रूप से किया जाता है, तो आपको फ़ंक्शन के शरीर के भीतर के प्रकार को निर्दिष्ट करना चाहिए।
कर्स्टन

क्या प्लेसहोल्डर प्रकार का Tउपयोग सभी में किया जाता है generic1()? आप उस फ़ंक्शन को कैसे कॉल करेंगे, ताकि कंपाइलर प्रकार का अनुमान लगा सके?
मार्टिन आर

3
मुझे उम्मीद है कि जेनेटिक 1 <blaClass> ("SomeString") जैसे फ़ंक्शन को कॉल करने का कुछ तरीका है
Greyisf

2
जेनरिक केवल उन मामलों के लिए नहीं है जब कंपाइलर संदर्भ का अनुमान लगा सकता है। स्पष्ट रूप से फ़ंक्शन को विशेषज्ञता देने से कोड के अन्य भागों को बांटा जा सकेगा। पूर्व: func foo<T>() -> T { ... }जो कुछ tप्रकार की वस्तु Tऔर वापसी के साथ करता है t। स्पष्ट रूप से विशेषज्ञता में घुसपैठ करने की Tअनुमति होगी । काश, इन तरह से भी फंक्शन्स को कॉल करने का एक तरीका होता। C # इसे अनुमति देता हैt1var t1 = foo<T>()
nacho4d

1
मैं अभी इसमें भाग गया हूं। यदि आप एक पैरामीटर के रूप में टाइप करने के लिए मजबूर हैं तो यह एक सामान्य फ़ंक्शन बनाने के लिए शून्य अर्थ देता है। यह एक बग सही होना चाहिए ?!
निक

जवाबों:


160

मुझे भी यह समस्या थी और मुझे अपने केस के लिए वर्कअराउंड मिला।

इस लेख में लेखक की भी यही समस्या है

https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide

तो समस्या यह है, लगता है कि संकलक को किसी भी तरह टी के प्रकार की आवश्यकता है। लेकिन इसे केवल जेनेरिक <प्रकार> (params ...) का उपयोग करने की अनुमति नहीं है।

आम तौर पर, कंपाइलर T के प्रकार को पैरामीटर प्रकारों को स्कैन करके देख सकता है क्योंकि यह वह जगह है जहां टी का उपयोग कई मामलों में किया जाता है।

मेरे मामले में यह थोड़ा अलग था, क्योंकि मेरे फ़ंक्शन का रिटर्न प्रकार T था। आपके मामले में ऐसा लगता है कि आपने अपने फ़ंक्शन में T का उपयोग बिल्कुल नहीं किया है। मुझे लगता है कि आपने उदाहरण कोड को सरल बनाया है।

इसलिए मेरे पास निम्नलिखित कार्य हैं

func getProperty<T>( propertyID : String ) -> T

और उदाहरण के लिए

getProperty<Int>("countProperty")

संकलक मुझे त्रुटि देता है:

स्पष्ट रूप से एक सामान्य कार्य का विशेषज्ञ नहीं हो सकता

इसलिए, कंपाइलर को T के प्रकार का पता लगाने के लिए जानकारी का एक और स्रोत देने के लिए, आपको स्पष्ट रूप से घोषित करना होगा कि रिटर्न वैल्यू किस प्रकार के वेरिएबल में सेव की गई है।

var value : Int = getProperty("countProperty")

इस तरह संकलक जानता है कि टी को पूर्णांक बनना है।

तो मुझे लगता है कि कुल मिलाकर इसका सीधा सा मतलब है कि यदि आप एक सामान्य फ़ंक्शन को निर्दिष्ट करते हैं, तो आपको अपने पैरामीटर प्रकारों में या वापसी प्रकार के रूप में कम से कम टी का उपयोग करना होगा।


2
मुझे एक टन समय बचा। धन्यवाद।
क्रिसलसन

4
क्या ऐसा करने का कोई तरीका है कि अगर कोई रिटर्न वैल्यू नहीं है? यानी,func updateProperty<T>( propertyID : String )
काइल बाशोर

1
मैं नहीं देखता कि आप ऐसा क्यों करना चाहते हैं? चूंकि आप कुछ भी नहीं लौटाते हैं, इसलिए आपके फ़ंक्शन को सामान्य घोषित करने में कोई लाभ नहीं है।
ThottChief

@ThottChief में बहुत लाभ है जैसे अगर आप कीपैथ का उपयोग कर रहे हैं, या स्ट्रिंग के रूप में टाइप नाम प्राप्त कर रहे हैं
zaitsman

शानदार जवाब धन्यवाद। काश यह इसके साथ काम करता, let value = foo() as? Typeतो इसका इस्तेमाल किया जा सकता था ifया guardयह परिणाम वैकल्पिक है, लेकिन यह नहीं है ...
एग्रीकल्चर ने

54

स्विफ्ट 5

आमतौर पर सामान्य कार्यों को परिभाषित करने के कई तरीके हैं। लेकिन वे ऐसी स्थिति पर आधारित होते हैं जिनका Tउपयोग एक parameter, या एक के रूप में किया जाना चाहिए return type

extension UIViewController {
    class func doSomething<T: UIView>() -> T {
        return T()
    }

    class func doSomethingElse<T: UIView>(value: T) {
        // Note: value is a instance of T
    }

    class func doLastThing<T: UIView>(value: T.Type) {
        // Note: value is a MetaType of T
    }
}

उसके बाद, हमें Tकॉल करते समय प्रदान करना होगा ।

let result = UIViewController.doSomething() as UIImageView // Define `T` by casting, as UIImageView
let result: UILabel = UIViewController.doSomething() // Define `T` with property type, as UILabel
UIViewController.doSomethingElse(value: UIButton()) // Define `T` with parameter type, as UIButton
UIViewController.doLastThing(value: UITextView.self) // Define `T` with parameter type, as UITextView

संदर्भ:

  1. http://austinzheng.com/2015/01/02/swift-generics-pt-1/
  2. https://dispatchswift.com/type-constraints-for-generics-in-swift-d6bf2f0dbbb2

सामान्य समस्या का शानदार जवाब ... क्योंकि इसे ठीक करने के कई तरीके हैं। मुझे यह भी एहसास हुआ कि self.result = UIViewController.doSomething()जब तक आप संपत्ति की घोषणा करते हैं, तब तक आप खुद ही काम करते हैं।
टेराडिल

बहुत सारी बातें बताते हुए शानदार जवाब।
ओखन ओबेय

जावा doLastThing<UITextView>()स्विफ्ट बन जाता है doLastThing(UITextView.self), जो है ... सबसे खराब नहीं, कम से कम। स्पष्ट रूप से जटिल परिणाम टाइप करने से बेहतर है। वर्कअराउंड के लिए धन्यवाद।
एरहानिस

18

समाधान वर्ग प्रकार को पैरामीटर के रूप में ले रहा है (जैसे जावा में)

संकलक को यह बताने के लिए कि वह किस प्रकार के वर्ग के साथ तर्क के रूप में काम कर रहा है

extension UIViewController {
    func navigate<ControllerType: UIViewController>(_ dump: ControllerType.Type, id: String, before: ((ControllerType) -> Void)?){
        let controller = self.storyboard?.instantiateViewController(withIdentifier: id) as! ControllerType
        before?(controller)
        self.navigationController?.pushViewController(controller, animated: true)
    }
}

इस रूप में कॉल करें:

self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: {
        controller in
        controller.user = self.notification.sender
    })

1
यह स्वीकृत उत्तर की तुलना में बहुत अच्छा और बेहतर है। कृपया ऐतिहासिक भाग को हटाने के लिए संपादन पर विचार करें, या कम से कम समाधान के नीचे रखें। धन्यवाद!
डैन रोसेनस्टार्क

1
प्रतिक्रिया के लिए :) @DanRosenstark धन्यवाद
Orkhan Alikhanov

हालांकि यह काम करता है मुझे नहीं लगता कि यह सबसे अच्छा अभ्यास है। कारण यह है कि अब फंक्शन डेफिनेशन निर्णय ले रहा है कि अगर कलाकारों के साथ कोई समस्या है तो क्या करें। तो, यहाँ आप बस दुर्घटनाग्रस्त हो रहे हैं यदि कलाकार विफल रहता है। ग्राहक को यह तय करने देना बेहतर है कि ग्राहक द्वारा अलग-अलग होने के बाद क्या करना है। इसलिए, मैं इस कारण से इस जवाब को वोट देने जा रहा हूं।
मुस्कानबोट

@smileBot आप उदाहरण उदाहरण बुरा या दृष्टिकोण है?
ओरखान अलीखानोव

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

4

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

जेनेरिक विधियों का उपयोग करना

func fetchObjectOrCreate<T: NSManagedObject>(type: T.Type) -> T {
    if let existing = fetchExisting(type) {
       return existing
    }
    else {
        return createNew(type)
    }
}

func fetchExisting<T: NSManagedObject>(type: T.Type) -> T {
    let entityName = NSStringFromClass(type)
     // Run query for entiry
} 

func createNew<T: NSManagedObject>(type: T.Type) -> T {
     let entityName = NSStringFromClass(type)
     // create entity with name
} 

एक सामान्य वर्ग का उपयोग करना (कम लचीला के रूप में जेनेरिक को केवल 1 प्रकार के लिए परिभाषित किया जा सकता है)

class Foo<T> {

   func doStuff(text: String) -> T {
      return doOtherStuff(text)
   }

   func doOtherStuff(text: String) -> T {

   }  

}

let foo = Foo<Int>()
foo.doStuff("text")

3

मुझे लगता है कि जब आप जेनेरिक फ़ंक्शन निर्दिष्ट करते हैं, तो आपको टाइप टी के कुछ मापदंडों को निर्दिष्ट करना चाहिए, जैसे कि निम्नलिखित:

func generic1<T>(parameter: T) {
    println("OK")
}

func generic2<T>(parameter: T) {
    generic1(parameter)
}

और यदि आप हैंडल () विधि को कॉल करना चाहते हैं, तो आप प्रोटोकॉल लिखकर, और टी के लिए प्रकार की बाधा को निर्दिष्ट करके ऐसा कर सकते हैं:

protocol Example {
    func handle() -> String
}

extension String: Example {
    func handle() -> String {
        return "OK"
    }
}

func generic1<T: Example>(parameter: T) {
    println(parameter.handle())
}

func generic2<T: Example>(parameter: T) {
    generic1(parameter)
}

इसलिए आप स्ट्रिंग के साथ इस सामान्य फ़ंक्शन को कॉल कर सकते हैं:

generic2("Some")

और यह संकलन करेगा


1

अब तक, मेरी व्यक्तिगत सर्वश्रेष्ठ प्रैक्टिस @ orhan-alikhanov का उत्तर है। आज, जब स्विफ्टयूआई को देख रहा है और कैसे .modifier()और ViewModifierइसे लागू किया गया है, तो मुझे एक और तरीका मिला (या क्या यह अधिक वर्कफ़ेयर है )?

बस दूसरे फ़ंक्शन को एक में लपेटें struct

उदाहरण:

यदि यह आपको "सामान्य कार्य के लिए स्पष्ट रूप से विशेषज्ञता नहीं दे सकता है"

func generic2<T>(name: String){
     generic1<T>(name)
}

यह एक मदद कर सकता है। लपेटें की घोषणा generic1एक में struct:

struct Generic1Struct<T> {
    func generic1(name: String) {## do, whatever it needs with T ##}
}

और इसके साथ कॉल करें:

func generic2<T>(name : String){
     Generic1Struct<T>().generic1(name: name)
}

टिप्पणियों:

  • मुझे नहीं पता कि यह किसी भी संभावित मामले में मदद करता है, जब यह त्रुटि संदेश आता है। मुझे सिर्फ इतना पता है, कि जब मैं आया था तो मैं कई बार फंस गया था। मुझे पता है, कि इस समाधान में आज मदद मिली, जब त्रुटि-संदेश आया।
  • जिस तरह से स्विफ्ट जेनेरिक संभालती है वह मेरे लिए अभी भी भ्रामक है।
  • यह उदाहरण और वर्कअराउंड structएक अच्छा उदाहरण है। यहां वर्कअराउंड में थोड़ी अधिक जानकारी नहीं है - लेकिन संकलक को पास करता है। समान जानकारी, लेकिन अलग परिणाम? फिर कुछ तो गड़बड़ है। यदि यह एक कंपाइलर-बग है तो यह ठीक हो सकता है।

0

मुझे अपने जेनेरिक क्लास फ़ंक्शन के साथ एक समान समस्या थी class func retrieveByKey<T: GrandLite>(key: String) -> T?

मैं इसे नहीं कह सकता let a = retrieveByKey<Categories>(key: "abc")जहाँ श्रेणियाँ GrandLite का एक उपवर्ग है।

let a = Categories.retrieveByKey(key:"abc")GrandLite लौटे, श्रेणियाँ नहीं। जेनेरिक फ़ंक्शंस क्लास के आधार पर टाइप नहीं करते हैं जो उन्हें कहते हैं।

class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?मुझे एक त्रुटि दी जब मैंने कोशिश की let a = Categories.retrieveByKey(aType: Categories, key: "abc")कि मुझे एक त्रुटि दी गई कि यह श्रेणियाँ नहीं बदल सके। हालांकि, ग्रैंडलाइट में श्रेणियाँ, भले ही श्रेणियाँ ग्रैंडलाइट का एक उपवर्ग हो। तथापि...

class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T? किया काम करता है, तो मैंने कोशिश की let a = Categories.retrieveByKey(aType: [Categories](), key: "abc")जाहिरा तौर पर एक उपवर्ग काम नहीं करता है की एक स्पष्ट काम है, लेकिन एक अंतर्निहित एक और सामान्य प्रकार (सरणी) का उपयोग कर असाइनमेंट स्विफ्ट 3 में काम करता है।


1
त्रुटि को ठीक करने के लिए, आपको खुद को डालने के बजाय, aTypeउदाहरण के रूप में प्रदान करना होगा । उदाहरण के लिए: । एक और उपाय परिभाषित है । फिर विधि को इस रूप में TCategorieslet a = Categories.retrieveByKey(aType: Categories(), key: "abc")aType: T.Typelet a = Categories.retrieveByKey(aType: Categories.self, key: "abc")
कहें
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.