एक इंटरफ़ेस बनाम एक सामान्य रूप से विवश प्रकार का उपयोग करने का कारण क्या है


15

ऑब्जेक्ट-ओरिएंटेड भाषाओं में, जो सामान्य प्रकार के मापदंडों का समर्थन करते हैं (वर्ग टेम्पलेट के रूप में भी जाना जाता है, और पैरामीट्रिक बहुरूपता, हालांकि प्रत्येक नाम अलग-अलग अर्थों को वहन करता है), अक्सर प्रकार पैरामीटर पर एक प्रकार की बाधा निर्दिष्ट करना संभव है, जैसे कि यह अवरोही हो। दूसरे प्रकार से। उदाहरण के लिए, यह C # में वाक्य रचना है:

//for classes:
class ExampleClass<T> where T : I1 {

}
//for methods:
S ExampleMethod<S>(S value) where S : I2 {
        ...
}

उन इंटरफेसों द्वारा विवश प्रकारों पर वास्तविक इंटरफ़ेस प्रकारों का उपयोग करने के क्या कारण हैं? उदाहरण के लिए, विधि हस्ताक्षर बनाने के क्या कारण हैं I2 ExampleMethod(I2 value)?


4
क्लास टेम्प्लेट (C ++) कुछ पूरी तरह से अलग हैं और खसरा जेनरिक की तुलना में अधिक शक्तिशाली हैं। हालांकि जेनरिक वाली भाषाएँ उनके लिए टेम्प्लेट सिंटैक्स उधार लेती हैं।
डेडुप्लिकेटर

इंटरफ़ेस विधियाँ अप्रत्यक्ष कॉल हैं, जबकि प्रकार विधियाँ प्रत्यक्ष कॉल हो सकती हैं। तो बाद वाला पूर्व की तुलना में तेज हो सकता है, और refमूल्य प्रकार के मापदंडों के मामले में , वास्तव में मूल्य प्रकार को संशोधित कर सकता है।
user541686

@ डेडप्लिकेटर: यह मानते हुए कि जेनरिक टेम्प्लेट से अधिक पुराने हैं, मैं यह देखने में विफल रहता हूं कि जेनेरिक ने सभी से, सिंटैक्स या अन्यथा टेम्प्लेट से कुछ भी उधार लिया हो सकता है।
जोर्ग डब्ल्यू मित्तग

3
@ JörgWMittag: मुझे संदेह है कि "ऑब्जेक्ट-ओरिएंटेड भाषाएँ जो जेनेरिक का समर्थन करती हैं" द्वारा, Deduplicator ने "ML और Ada" के बजाय "Java और C #" को समझा होगा। फिर पूर्व में C ++ से प्रभाव स्पष्ट है, इसके बावजूद कि सभी भाषाएं जेनेरिक या पैरामीट्रिक बहुरूपता नहीं हैं, जो C ++ से उधार ली गई हैं।
स्टीव जेसोप

2
@SteveJessop: ML, Ada, Eiffel, Haskell predate C ++ टेम्प्लेट, Scala, F #, OCaml के बाद आया, और उनमें से कोई भी C ++ का सिंटैक्स साझा नहीं करता है। (दिलचस्प है, यहां तक ​​कि डी, जो सी ++ से बहुत अधिक उधार लेता है, विशेष रूप से टेम्पलेट्स, सी ++ के वाक्यविन्यास को साझा नहीं करता है।) "जावा और सी #" "सामान्य भाषाएं" वाली भाषाओं का एक संकीर्ण दृष्टिकोण है, मुझे लगता है।
जोर्ग डब्ल्यू मित्तग

जवाबों:


21

पैरामीट्रिक संस्करण का उपयोग करके देता है

  1. फ़ंक्शन के उपयोगकर्ताओं को अधिक जानकारी
  2. आपके द्वारा लिखे जाने वाले कार्यक्रमों की संख्या में कमी (मुफ्त बग जाँच)

एक यादृच्छिक उदाहरण के रूप में, मान लें कि हमारे पास एक विधि है जो एक द्विघात समीकरण की जड़ों की गणना करती है

int solve(int a, int b, int c) {
  // My 7th grade math teacher is laughing somewhere
}

और फिर आप चाहते हैं कि यह अन्य प्रकार की संख्याओं पर काम करे, इसके अलावा चीजों की तरह int। आप कुछ ऐसा लिख ​​सकते हैं

Num solve(Num a, Num b, Num c){
  ...
}

मुद्दा यह है कि यह वह नहीं है जो आप इसे चाहते हैं। इसे कहते हैं

मुझे कोई भी 3 चीजें दें जो संख्या की तरह हैं (जरूरी नहीं कि उसी तरह से) और मैं आपको कुछ प्रकार की संख्या वापस दूंगा

हम ऐसा कुछ नहीं कर सकते int sol = solve(a, b, c)अगर a, bऔर हम कर cरहे हैं intक्योंकि हम नहीं जानते कि विधि intअंत में वापस लौटने वाली है ! यह कुछ अजीब नृत्य के साथ डाउनकास्टिंग और प्रार्थना की ओर जाता है अगर हम समाधान को बड़े अभिव्यक्ति में उपयोग करना चाहते हैं।

फ़ंक्शन के अंदर, कोई व्यक्ति हमें एक फ़्लोट, एक बिगिंट और डिग्री सौंप सकता है और हमें उन्हें एक साथ जोड़ना और गुणा करना होगा। हम इसे वैधानिक रूप से अस्वीकार करना चाहते हैं क्योंकि इन 3 वर्गों के बीच संचालन अस्पष्ट होने वाला है। डिग्री 360 मॉड हैं, इसलिए यह ऐसा नहीं होगा जो a.plus(b) = b.plus(a)और इसी तरह की उल्लंघनाएं उत्पन्न होंगी।

अगर हम उप-प्रकार के साथ पैरामीट्रिक बहुरूपता का उपयोग करते हैं तो हम इस सब पर शासन कर सकते हैं क्योंकि हमारा प्रकार वास्तव में कहता है कि हमारा क्या मतलब है

<T : Num> T solve(T a, T b, T c)

या शब्दों में "यदि आप मुझे कुछ प्रकार देते हैं जो एक संख्या है, तो मैं उन गुणांकों के साथ समीकरणों को हल कर सकता हूं"।

यह कई अन्य स्थानों पर भी आता है। उदाहरण के लिए एक और अच्छा स्रोत कार्य करता है जो अमूर्त कंटेनर, आला किसी प्रकार का अधिक कर रहे हैं reverse, sort, map, आदि


8
सारांश में, सामान्य संस्करण गारंटी देता है कि सभी तीन इनपुट (और आउटपुट) एक ही प्रकार की संख्या होगी।
मैथमेटिकलऑर्चिड

हालाँकि, यह तब कम हो जाता है जब आप प्रश्न के प्रकार को नियंत्रित नहीं करते हैं (और इस प्रकार इसमें कोई इंटरफ़ेस नहीं जोड़ा जा सकता है)। अधिकतम सामान्यता के लिए आपको तर्क प्रकार (जैसे Num<int>) द्वारा एक इंटरफ़ेस पैराट्राइक को अतिरिक्त तर्क के रूप में स्वीकार करना होगा । आप हमेशा प्रतिनिधिमंडल के माध्यम से किसी भी प्रकार के इंटरफ़ेस को लागू कर सकते हैं। यह अनिवार्य रूप से हास्केल के प्रकार की कक्षाएं हैं, जो उपयोग करने के लिए बहुत अधिक थकाऊ हैं, क्योंकि आपको इंटरफ़ेस के आसपास स्पष्ट रूप से पास करना होगा।
डोभाल

16

उन इंटरफेसों द्वारा विवश प्रकारों पर वास्तविक इंटरफ़ेस प्रकारों का उपयोग करने के क्या कारण हैं?

क्योंकि यही आपको चाहिए ...

IFoo Fn(IFoo x);
T Fn<T>(T x) where T: IFoo;

दो निश्चित रूप से अलग हस्ताक्षर हैं। पहले इंटरफ़ेस को लागू करने वाला कोई भी प्रकार लेता है और केवल इसकी गारंटी देता है कि रिटर्न मान इंटरफ़ेस को संतुष्ट करता है।

दूसरा इंटरफ़ेस को लागू करने में किसी भी प्रकार का लेता है और गारंटी देता है कि यह कम से कम उस प्रकार को फिर से लौटाएगा (बजाय कुछ ऐसा जो कम प्रतिबंधात्मक इंटरफ़ेस को संतुष्ट करता है)।

कभी-कभी, आप कमजोर गारंटी चाहते हैं। कभी-कभी आप मजबूत चाहते हैं।


क्या आप एक उदाहरण दे सकते हैं कि आप कमजोर गारंटी संस्करण का उपयोग कैसे करेंगे?
ग्रिगोरोस

4
@GregRos - उदाहरण के लिए, कुछ पार्सर कोड में मैंने लिखा था। मुझे एक फ़ंक्शन मिला है Orजिसमें दो Parserऑब्जेक्ट्स (एक सार आधार वर्ग, लेकिन सिद्धांत रखता है) और एक नया Parser(लेकिन एक अलग प्रकार के साथ) देता है। अंतिम उपयोगकर्ता को यह पता नहीं होना चाहिए कि क्या ठोस प्रकार है।
तेलस्टिन

C # में मैं कल्पना करता हूं कि जो टी पास किया गया था उसके अलावा एक टी वापस आ रहा है जो नए अवरोध के बिना लगभग असंभव (w / o परावर्तन दर्द) है और साथ ही साथ आपकी मजबूत गारंटी को स्वयं बेकार कर रहा है।
NtscCobalt

1
@NtscCobalt: जब आप पैरामीट्रिक और इंटरफ़ेस जेनेरिक प्रोग्रामिंग दोनों को जोड़ते हैं तो यह अधिक उपयोगी होता है। जैसे कि LINQ हर समय क्या करता है (एक को स्वीकार करता है IEnumerable<T>, एक और देता है IEnumerable<T>जो कि वास्तव में उदाहरण के लिए है OrderedEnumerable<T>)
बेन वोइगट

2

विधि मापदंडों के लिए विवश जेनरिक का उपयोग किसी विधि को पारित की गई चीज़ के आधार पर उसके रिटर्न प्रकार के लिए अनुमति दे सकता है। .NET में उनके पास अतिरिक्त लाभ भी हो सकते हैं। उनमें से:

  1. एक विधि जो एक refया outपैरामीटर के रूप में एक विवश सामान्य को स्वीकार करती है उसे एक चर पारित किया जा सकता है जो बाधा को संतुष्ट करता है; इसके विपरीत, इंटरफ़ेस-प्रकार पैरामीटर के साथ एक गैर-जेनेरिक विधि उस सटीक इंटरफ़ेस प्रकार के चर को स्वीकार करने तक सीमित होगी।

  2. जेनेरिक प्रकार पैरामीटर टी के साथ एक विधि टी। के सामान्य संग्रह को स्वीकार कर सकती है। एक विधि जो स्वीकार करती है वह एक IList<T> where T:IAnimalको स्वीकार करने में सक्षम होगी List<SiameseCat>, लेकिन एक विधि जो IList<Animal>ऐसा नहीं चाहती थी ।

  3. एक बाधा कभी-कभी सामान्य प्रकार के संदर्भ में एक इंटरफ़ेस निर्दिष्ट कर सकती है, जैसे where T:IComparable<T>

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

  5. एक सामान्य पैरामीटर में कई अड़चनें हो सकती हैं, जबकि "कोई प्रकार जो IFU और IBAR दोनों को लागू करता है" के पैरामीटर को निर्दिष्ट करने का कोई अन्य तरीका नहीं है। कभी-कभी यह एक दोधारी तलवार हो सकती है, क्योंकि कोड जिसे प्रकार का एक पैरामीटर प्राप्त हुआ है, IFooउसे इस तरह से पारित करने के लिए बहुत ही कठिन मिलेगा, जिससे डबल-बाध्य जेनरिक की उम्मीद हो, भले ही प्रश्न में उदाहरण सभी बाधाओं को पूरा करेगा।

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

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