Covariance और Contra-variance के बीच अंतर


148

मुझे कोवरियन और कंट्रोवर्सी के बीच के अंतर को समझने में परेशानी हो रही है।

जवाबों:


266

सवाल यह है कि "कोविरियन और कंट्रोवर्सी में क्या अंतर है?"

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

सभी सी # प्रकार के सेट के निम्नलिखित दो सबसेट पर विचार करें। प्रथम:

{ Animal, 
  Tiger, 
  Fruit, 
  Banana }.

और दूसरा, यह स्पष्ट रूप से संबंधित सेट:

{ IEnumerable<Animal>, 
  IEnumerable<Tiger>, 
  IEnumerable<Fruit>, 
  IEnumerable<Banana> }

पहले सेट से दूसरे सेट तक एक मैपिंग ऑपरेशन होता है। अर्थात्, पहले सेट में प्रत्येक T के लिए, दूसरे सेट में संबंधित प्रकार है IEnumerable<T>। या, संक्षिप्त रूप में, मानचित्रण है T → IE<T>। ध्यान दें कि यह एक "पतला तीर" है।

मेरे साथ इतनी दूर?

अब एक संबंध पर विचार करते हैं । पहले सेट में प्रकारों के जोड़े के बीच एक असाइनमेंट संगतता संबंध है । प्रकार का एक मान प्रकार के Tigerएक चर को सौंपा जा सकता है Animal, इसलिए इन प्रकारों को "असाइनमेंट संगत" कहा जाता है। आइए लिखते हैं "एक प्रकार के एक मूल्य Xको एक प्रकार के एक चर को सौंपा जा सकता है Y" X ⇒ Y। ध्यान दें कि यह एक "वसा तीर" है।

तो हमारे पहले सबसेट में, यहाँ सभी असाइनमेंट संगतता रिश्ते हैं:

Tiger   Tiger
Tiger   Animal
Animal  Animal
Banana  Banana
Banana  Fruit
Fruit   Fruit

C # 4 में, जो कुछ इंटरफेस के सहवर्ती कार्य संगतता का समर्थन करता है, दूसरे सेट में प्रकारों के जोड़े के बीच असाइनमेंट संगतता संबंध है:

IE<Tiger>   IE<Tiger>
IE<Tiger>   IE<Animal>
IE<Animal>  IE<Animal>
IE<Banana>  IE<Banana>
IE<Banana>  IE<Fruit>
IE<Fruit>   IE<Fruit>

ध्यान दें कि मानचित्रण T → IE<T> असाइनमेंट संगतता के अस्तित्व और दिशा को संरक्षित करता है । यही है, अगर X ⇒ Y, तो यह भी सच है IE<X> ⇒ IE<Y>

यदि हमारे पास वसा तीर के दोनों ओर दो चीजें हैं, तो हम दोनों पक्षों को एक समान पतले तीर के दाहिने हाथ की ओर से कुछ के साथ बदल सकते हैं।

एक मैपिंग जिसमें एक विशेष संबंध के संबंध में यह संपत्ति होती है, उसे "सहसंयोजक मानचित्रण" कहा जाता है। यह समझ में आना चाहिए: टाइगर्स के एक अनुक्रम का उपयोग किया जा सकता है जहां जानवरों के अनुक्रम की आवश्यकता होती है, लेकिन विपरीत सच नहीं है। जानवरों के एक अनुक्रम का उपयोग नहीं किया जा सकता है जहां बाघों के अनुक्रम की आवश्यकता होती है।

वह कोविरेंस है। अब सभी प्रकार के सेट के इस सबसेट पर विचार करें:

{ IComparable<Tiger>, 
  IComparable<Animal>, 
  IComparable<Fruit>, 
  IComparable<Banana> }

अब हमारे पास पहले सेट से तीसरे सेट तक मैपिंग है T → IC<T>

C # 4 में:

IC<Tiger>   IC<Tiger>
IC<Animal>  IC<Tiger>     Backwards!
IC<Animal>  IC<Animal>
IC<Banana>  IC<Banana>
IC<Fruit>   IC<Banana>     Backwards!
IC<Fruit>   IC<Fruit>

यही है, मानचित्रण T → IC<T>ने अस्तित्व को संरक्षित किया है लेकिन असाइनमेंट संगतता की दिशा को उलट दिया है । वह है, अगर X ⇒ Y, तो IC<X> ⇐ IC<Y>

एक मानचित्रण जो संरक्षित करता है, लेकिन एक संबंध को उलट देता है, एक contravariant मानचित्रण कहलाता है ।

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

तो यह है कि C # 4 में कोविरियन और कंट्रोवर्सी के बीच का अंतर है। कोवरियनस एसेसेबिलिटी की दिशा को बनाए रखता है। Contravariance पराजयों यह।


4
मेरे जैसे किसी व्यक्ति के लिए, उदाहरणों को जोड़ना बेहतर होगा कि क्या सहसंयोजक नहीं है और क्या अंतर्विरोधी नहीं है और क्या दोनों नहीं है।
भजन

2
@ बरगिट्टा: यह बहुत समान है। अंतर यह है कि C # निश्चित साइट संस्करण का उपयोग करता है और जावा कॉल साइट संस्करण का उपयोग करता है । इसलिए जिस तरह से चीजें बदलती हैं, वही होती है, लेकिन जहां डेवलपर कहता है कि "मुझे इसके वेरिएंट होने की जरूरत है" अलग है। संयोग से, दोनों भाषाओं में सुविधा एक ही व्यक्ति द्वारा डिज़ाइन की गई थी!
एरिक लिपर्ट

2
@AishishNegi: "के रूप में इस्तेमाल किया जा सकता है" के रूप में तीर पढ़ें। "एक चीज जो जानवरों की तुलना कर सकती है वह एक ऐसी चीज के रूप में इस्तेमाल की जा सकती है जो बाघों की तुलना कर सकती है"। अब समझ में आता है?
एरिक लिपर्ट

1
@ आशीषनेगी: नहीं, यह सही नहीं है। IEnumerable covariant है क्योंकि T केवल IEnumerable के तरीकों के रिटर्न में दिखाई देता है। और IComparable contravariant है क्योंकि T केवल IComparable के तरीकों के औपचारिक मापदंडों के रूप में प्रकट होता है
एरिक लिपर्ट

2
@ आशीषनेगी: आप उन तार्किक कारणों के बारे में सोचना चाहते हैं जो इन रिश्तों से गुजरते हैं । क्यों हम में बदल सकते हैं IEnumerable<Tiger>करने के लिए IEnumerable<Animal>सुरक्षित रूप से? क्योंकि जिराफ को इनपुट करने का कोई तरीका नहीं है IEnumerable<Animal>। क्यों हम एक परिवर्तित कर सकते हैं IComparable<Animal>करने के लिए IComparable<Tiger>? क्योंकि जिराफ को बाहर निकालने का कोई तरीका नहीं है IComparable<Animal>। सही बात?
एरिक लिपर्ट

111

उदाहरण देना शायद सबसे आसान है - यह निश्चित रूप से है कि मैं उन्हें कैसे याद करता हूं।

सहप्रसरण

विहित उदाहरण: IEnumerable<out T>,Func<out T>

आप से परिवर्तित कर सकते हैं IEnumerable<string>करने के लिए IEnumerable<object>, या Func<string>करने के लिए Func<object>। इन वस्तुओं से ही मूल्य निकलते हैं।

यह काम करता है क्योंकि यदि आप केवल API से मान निकाल रहे हैं, और यह कुछ विशिष्ट (जैसे string) वापस करने जा रहा है , तो आप उस लौटे मान को अधिक सामान्य प्रकार (जैसे object) मान सकते हैं ।

contravariance

विहित उदाहरण: IComparer<in T>,Action<in T>

आप से परिवर्तित कर सकते हैं IComparer<object>करने के लिए IComparer<string>, या Action<object>करने के लिए Action<string>; मूल्यों केवल जाना में इन वस्तुओं।

इस बार यह काम करता है क्योंकि अगर एपीआई कुछ सामान्य (जैसे object) की उम्मीद कर रहा है, तो आप इसे कुछ और अधिक विशिष्ट (जैसे string) दे सकते हैं ।

आम तौर पर

आप एक इंटरफेस है, तो IFoo<T>उस में covariant हो सकता है T(यानी यह घोषणा के रूप में IFoo<out T>करता है, तो Tकेवल (जैसे एक वापसी प्रकार) इंटरफ़ेस के भीतर एक निर्गम स्थिति में प्रयोग किया जाता है। यह में contravariant हो सकता है T(यानी IFoo<in T>) यदि Tकेवल एक इनपुट स्थिति में प्रयोग किया जाता है ( जैसे एक पैरामीटर प्रकार)।

यह संभावित रूप से भ्रामक हो जाता है क्योंकि "आउटपुट पोजिशन" बहुत सरल नहीं है क्योंकि यह लगता है - प्रकार Action<T>का एक पैरामीटर अभी भी केवल Tएक आउटपुट पोजीशन में उपयोग कर रहा है - Action<T>यह दौर के विपरीत , यदि आप देखते हैं कि मेरा क्या मतलब है। यह एक "आउटपुट" है जिसमें मान कॉलर के कोड की ओर पद्धति के कार्यान्वयन से गुजर सकते हैं , ठीक उसी तरह जैसे रिटर्न वैल्यू कर सकते हैं। आमतौर पर इस तरह की बात नहीं आती है, सौभाग्य से :)


1
मेरे जैसे किसी व्यक्ति के लिए, उदाहरणों को जोड़ना बेहतर होगा कि क्या सहसंयोजक नहीं है और क्या अंतर्विरोधी नहीं है और क्या दोनों नहीं है।
बजन

1
@ जीन स्कीट अच्छा उदाहरण है, मुझे केवल यह समझ में नहीं आता है "एक प्रकार Action<T>का पैरामीटर अभी भी केवल Tएक आउटपुट स्थिति में उपयोग कर रहा है"Action<T>वापसी प्रकार शून्य है, यह Tआउटपुट के रूप में कैसे उपयोग कर सकता है ? या यह है कि इसका क्या मतलब है, क्योंकि यह कुछ भी नहीं देता है जिसे आप देख सकते हैं कि यह कभी भी नियम का उल्लंघन नहीं कर सकता है?
अलेक्जेंडर डर्क

2
मेरे भविष्य के स्वयं के लिए, जो अंतर को फिर से जारी करने के लिए इस उत्कृष्ट उत्तर पर वापस आ रहा है , यह वह पंक्ति है जो आप चाहते हैं: "[कोविरियन] काम करता है क्योंकि यदि आप केवल एपीआई से बाहर मान ले रहे हैं, और यह कुछ वापस करने जा रहा है विशिष्ट (स्ट्रिंग की तरह), आप उस लौटे हुए मूल्य को अधिक सामान्य प्रकार (जैसे ऑब्जेक्ट) के रूप में मान सकते हैं। "
मैट क्लेन

इस सब में सबसे अधिक भ्रमित करने वाला हिस्सा यह है कि या तो सहसंयोजक या विरोधाभासी के लिए, यदि आप दिशा (या बाहर) की उपेक्षा करते हैं, तो आप वैसे भी अधिक सामान्य रूपांतरण के लिए और अधिक विशिष्ट हो जाते हैं! मेरा मतलब है: के लिए "आपको लगता है कि दिए गए मान एक अधिक सामान्य प्रकार (वस्तु की तरह) के रूप में इलाज कर सकते हैं" सहप्रसरण के लिए "एपीआई कुछ सामान्य (वस्तु) की तरह उम्मीद है कि आप इसे कुछ और अधिक विशिष्ट (स्ट्रिंग की तरह) दे सकते हैं": और contravariance । मेरे लिए ये उसी तरह की ध्वनि है!
एक्समैट

@AlexanderDerck: मुझे यकीन नहीं है कि मैंने आपको पहले जवाब क्यों नहीं दिया; मैं मानता हूं कि यह अस्पष्ट है, और इसे स्पष्ट करने का प्रयास करेंगे।
जॉन स्कीट

16

मुझे उम्मीद है कि मेरी पोस्ट विषय की भाषा-अज्ञेयवादी दृष्टिकोण प्राप्त करने में मदद करती है।

हमारे आंतरिक प्रशिक्षण के लिए मैंने अद्भुत पुस्तक "स्मालटाक, ऑब्जेक्ट्स एंड डिज़ाइन (चामोंड लिउ)" के साथ काम किया है और मैंने निम्नलिखित उदाहरणों को पुनःप्रकाशित किया है।

"संगति" का क्या अर्थ है? विचार उच्च-प्रकार के प्रकार के साथ सुरक्षित प्रकार की पदानुक्रमों को डिजाइन करने के लिए है। यदि आप एक वैधानिक रूप से टाइप की गई भाषा में काम करते हैं, तो इस स्थिरता को प्राप्त करने की कुंजी उप-प्रकार आधारित अनुरूपता है। (हम यहां उच्च स्तर पर लिसकोव प्रतिस्थापन सिद्धांत (एलएसपी) पर चर्चा करेंगे।)

व्यावहारिक उदाहरण (C # में छद्म कोड / अमान्य):

  • सहसंयोजक: मान लें कि पक्षी जो "टाइपिंग" लगातार स्थैतिक रूप से अंडे देते हैं: यदि पक्षी बर्ड एग देता है, तो क्या बर्ड का उपप्रकार एग का उपप्रकार नहीं रखेगा? उदा। प्रकार डक एक डक लेग देता है, फिर संगति दी जाती है। यह सुसंगत क्यों है? क्योंकि इस तरह की अभिव्यक्ति में: Egg anEgg = aBird.Lay();संदर्भ बर्ड को कानूनी रूप से एक बर्ड या डक उदाहरण द्वारा प्रतिस्थापित किया जा सकता है। हम कहते हैं कि वापसी प्रकार, प्रकार में सहसंयोजक है, जिसमें ले () को परिभाषित किया गया है। एक उप-प्रकार का ओवरराइड एक अधिक विशिष्ट प्रकार लौटा सकता है। => "वे अधिक वितरित करते हैं।"

  • कंट्रोवर्सी: मान लीजिए कि पियानोस स्थिर टाइपिंग के साथ पियानोवादक "लगातार" खेल सकते हैं: यदि एक पियानोवादक पियानो बजाता है, तो क्या वह ग्रैंडपियानो खेलने में सक्षम होगा? बल्कि एक कलाप्रवीण व्यक्ति एक GrandPiano नहीं खेलेंगे? (चेतावनी दी है, एक मोड़ है!) यह असंगत है! क्योंकि ऐसी अभिव्यक्ति में: aPiano.Play(aPianist);पियानो को कानूनी रूप से पियानो या ग्रैंडपियानो उदाहरण द्वारा प्रतिस्थापित नहीं किया जा सकता है! एक GrandPiano केवल एक Virtuoso द्वारा खेला जा सकता है, पियानोवादक बहुत सामान्य हैं! GrandPianos को अधिक सामान्य प्रकारों द्वारा बजाने योग्य होना चाहिए, फिर नाटक सुसंगत है। हम कहते हैं कि पैरामीटर प्रकार प्रकार के विपरीत है, जिसमें प्ले () परिभाषित है। एक उपप्रकार के ओवरराइड एक अधिक सामान्यीकृत प्रकार को स्वीकार कर सकते हैं। => "उन्हें कम आवश्यकता होती है।"

C # पर वापस:
क्योंकि C # मूल रूप से एक टाइप की गई भाषा है, एक प्रकार के इंटरफ़ेस का "स्थान" जो कि सह-या विपरीतार्थी होना चाहिए (जैसे पैरामीटर और रिटर्न प्रकार), उस प्रकार के निरंतर उपयोग / विकास की गारंटी देने के लिए स्पष्ट रूप से चिह्नित होना चाहिए। , एलएसपी काम ठीक करने के लिए। गतिशील रूप से टाइप की जाने वाली भाषाओं में एलएसपी स्थिरता आमतौर पर एक समस्या नहीं है, दूसरे शब्दों में आप पूरी तरह से .Net इंटरफेस और डेलिगेट्स पर "और" कॉन्ट्रोवर्शियल "मार्कअप" से छुटकारा पा सकते हैं, यदि आपने केवल अपने प्रकारों में टाइप डायनेमिक का उपयोग किया है। - लेकिन यह C # में सबसे अच्छा समाधान नहीं है (आपको सार्वजनिक इंटरफेस में गतिशील का उपयोग नहीं करना चाहिए)।

सिद्धांत पर वापस:
वर्णित अनुरूपता (सहसंयोजक वापसी प्रकार / कंट्राविरेंट पैरामीटर प्रकार) सैद्धांतिक आदर्श है (भाषाओं पन्ना और पीओएल -1 द्वारा समर्थित)। कुछ ऊप भाषाओं (जैसे एफिल) ने एक और प्रकार की संगति, एस्प लागू करने का निर्णय लिया। सहसंयोजक पैरामीटर प्रकार भी, क्योंकि यह सैद्धांतिक आदर्श की तुलना में वास्तविकता का बेहतर वर्णन करता है। सांख्यिकीय रूप से टाइप की गई भाषाओं में "डबल डिस्पैचिंग" और "विजिटर" जैसे डिज़ाइन पैटर्न के अनुप्रयोग द्वारा वांछित स्थिरता अक्सर प्राप्त की जानी चाहिए। अन्य भाषाएँ तथाकथित "कई प्रेषण" या बहु विधियाँ प्रदान करती हैं (यह मूल रूप से रन समय में फ़ंक्शन अधिभार का चयन करती है , जैसे CLOS के साथ) या डायनामिक टाइपिंग का उपयोग करके वांछित प्रभाव प्राप्त करें।


आप कहते हैं कि एक उप-प्रकार का ओवरराइड अधिक विशिष्ट प्रकार लौटा सकता है । लेकिन वह पूरी तरह से असत्य है। यदि Birdपरिभाषित करता है public abstract BirdEgg Lay();, तो लागू होना Duck : Bird चाहिएpublic override BirdEgg Lay(){} ताकि आपके दावे BirdEgg anEgg = aBird.Lay();में किसी भी प्रकार का विचरण हो, बस असत्य है। स्पष्टीकरण के बिंदु का आधार होने के नाते, पूरा बिंदु अब चला गया है। आप चाहेंगे के बजाय का कहना है कि सहप्रसरण कार्यान्वयन जहां एक DuckEgg परोक्ष बाहर / वापसी प्रकार BirdEgg को डाला जाता है के भीतर मौजूद है? किसी भी तरह, कृपया मेरा भ्रम दूर करें।
सुमेरे

1
इसे छोटा करने के लिए: आप सही हैं! गलतफहमी के लिए खेद है। C # केDuckEgg Lay() लिए एक वैध ओवरराइड नहीं है , और यह क्रूक्स है। C # covariant रिटर्न प्रकारों का समर्थन नहीं करता है, लेकिन Java के साथ-साथ C ++ भी करता है। मैंने C # के समान सिंटैक्स का उपयोग करके सैद्धांतिक आदर्श का वर्णन किया। C # में आपको बर्ड और डक को एक सामान्य इंटरफ़ेस लागू करने की आवश्यकता होती है, जिसमें ले को परिभाषित किया जाता है कि एक सहसंयोजक वापसी (यानी आउट-स्पेसिफिकेशन) प्रकार है, फिर मामले एक साथ फिट होते हैं! Egg Lay()
निको

1
@ जॉन-स्कीट के उत्तर पर मैट-क्लेन की टिप्पणी के एनालॉग के रूप में, "मेरे भविष्य के स्वयं के लिए": मेरे लिए यहां सबसे अच्छा उपाय है "वे अधिक वितरित करते हैं" (विशिष्ट) और "उन्हें कम" (विशिष्ट) की आवश्यकता होती है। "कम की आवश्यकता है और अधिक वितरित करें" एक उत्कृष्ट महामारी है! यह एक नौकरी के अनुरूप है जहां मुझे कम-विशिष्ट निर्देशों (सामान्य अनुरोधों) की आवश्यकता होती है और फिर भी कुछ और अधिक विशिष्ट (एक वास्तविक कार्य-उत्पाद) देने की आवश्यकता होती है। किसी भी तरह से उपप्रकारों (एलएसपी) का क्रम अखंड नहीं है।
करफस

@karfus: धन्यवाद, लेकिन जैसा कि मुझे याद है कि मैंने एक और स्रोत से "आवश्यकता कम और अधिक देने" के विचार को विरोधाभासी बताया। यह हो सकता है कि लियू की पुस्तक मैं ऊपर उल्लेख कर रहा हूं ... या यहां तक ​​कि एक .NET रॉक बात भी है। Btw। जावा में, लोगों ने महामारी को "PECS" में कम कर दिया, जो सीधे रूप से भिन्न रूप में घोषणा करने से संबंधित है, PECS "निर्माता extends, उपभोक्ता super" के लिए है।
निको

5

कनवर्टर प्रतिनिधि मुझे अंतर समझने में मदद करता है।

delegate TOutput Converter<in TInput, out TOutput>(TInput input);

TOutputसहसंयोजक का प्रतिनिधित्व करता है जहां एक विधि अधिक विशिष्ट प्रकार देता है

TInputविरोधाभासी का प्रतिनिधित्व करता है जहां एक विधि एक कम विशिष्ट प्रकार से पारित की जाती है ।

public class Dog { public string Name { get; set; } }
public class Poodle : Dog { public void DoBackflip(){ System.Console.WriteLine("2nd smartest breed - woof!"); } }

public static Poodle ConvertDogToPoodle(Dog dog)
{
    return new Poodle() { Name = dog.Name };
}

List<Dog> dogs = new List<Dog>() { new Dog { Name = "Truffles" }, new Dog { Name = "Fuzzball" } };
List<Poodle> poodles = dogs.ConvertAll(new Converter<Dog, Poodle>(ConvertDogToPoodle));
poodles[0].DoBackflip();

0

सह और कॉन्ट्रा विचरण काफी तार्किक चीजें हैं। भाषा प्रकार प्रणाली हमें वास्तविक जीवन तर्क का समर्थन करने के लिए मजबूर करती है। उदाहरण द्वारा समझना आसान है।

सहप्रसरण

उदाहरण के लिए आप एक फूल खरीदना चाहते हैं और आपके शहर में दो फूलों की दुकान है: गुलाब की दुकान और डेज़ी की दुकान।

यदि आप किसी से पूछते हैं "फूलों की दुकान कहाँ है?" और कोई आपको बताता है कि गुलाब की दुकान कहां है, क्या यह ठीक होगा? हां, क्योंकि गुलाब एक फूल है, अगर आप एक फूल खरीदना चाहते हैं तो आप गुलाब खरीद सकते हैं। वही लागू होता है यदि किसी ने आपको डेज़ी शॉप के पते के साथ उत्तर दिया।

यह सहसंयोजकता का उदाहरण है : यदि आप जेनेरिक मान (फ़ंक्शन से परिणाम के रूप में रिटर्न) पैदा करते हैं , तो आपको कास्ट A<C>करने की अनुमति है A<B>, जहां Cउप-वर्ग है । Covariance उत्पादकों के बारे में है, इसीलिए C # covariance के लिए कीवर्ड का उपयोग करते हैं ।BAout

प्रकार:

class Flower {  }
class Rose: Flower { }
class Daisy: Flower { }

interface FlowerShop<out T> where T: Flower {
    T getFlower();
}

class RoseShop: FlowerShop<Rose> {
    public Rose getFlower() {
        return new Rose();
    }
}

class DaisyShop: FlowerShop<Daisy> {
    public Daisy getFlower() {
        return new Daisy();
    }
}

प्रश्न "फूलों की दुकान कहाँ है?", उत्तर "गुलाब की दुकान है":

static FlowerShop<Flower> tellMeShopAddress() {
    return new RoseShop();
}

contravariance

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

यह विरोधाभासी का एक उदाहरण है : यदि आपको जेनेरिक मूल्य का उपभोग करने की अनुमति A<B>है A<C>, जहां आपको Cउप-वर्ग में प्रवेश करने की अनुमति है । उपभोक्ताओं के बारे में विरोधाभासी है, यही कारण है कि सी # कंट्रोवर्सी के लिए कीवर्ड का उपयोग करते हैं ।BAin

प्रकार:

interface PrettyGirl<in TFavoriteFlower> where TFavoriteFlower: Flower {
    void takeGift(TFavoriteFlower flower);
}

class AnyFlowerLover: PrettyGirl<Flower> {
    public void takeGift(Flower flower) {
        Console.WriteLine("I like all flowers!");
    }
}

आप अपनी प्रेमिका पर विचार कर रहे हैं, जो किसी भी फूल को प्यार करती है, जो गुलाब से प्यार करता है, और उसे गुलाब दे रहा है:

PrettyGirl<Rose> girlfriend = new AnyFlowerLover();
girlfriend.takeGift(new Rose());

लिंक

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