C # में Covariant और Contravariant इंटरफेस को समझना


86

मैं सी # पर एक पाठ्यपुस्तक में इन पर आया हूं, लेकिन मुझे उन्हें समझने में कठिनाई हो रही है, शायद संदर्भ की कमी के कारण।

क्या वे क्या कर रहे हैं और क्या वे वहाँ के लिए उपयोगी हैं की एक अच्छी संक्षिप्त व्याख्या है?

स्पष्टीकरण के लिए संपादित करें:

सहसंयोजक इंटरफ़ेस:

interface IBibble<out T>
.
.

कंट्रावेरिएंट इंटरफ़ेस:

interface IBibble<in T>
.
.

3
यह एक छोटा और अच्छा विस्तार है IMHO: blogs.msdn.com/csharpfaq/archive/2010/02/16/…
digEmAll

उपयोगी हो सकता है: ब्लॉग पोस्ट
Krunal

हम्म यह अच्छा है, लेकिन यह नहीं समझाता है कि जो मुझे वास्तव में चकित करता है वह क्यों है।
निब्लीपिग

जवाबों:


144

इसके साथ <out T>, आप इंटरफ़ेस संदर्भ को पदानुक्रम में एक ऊपर की तरफ मान सकते हैं।

इसके साथ <in T>, आप hi hi में इंटरफ़ेस संदर्भ को एक नीचे की ओर मान सकते हैं।

मुझे इसे और अधिक अंग्रेजी शब्दों में समझाने की कोशिश करते हैं।

मान लीजिए कि आप अपने चिड़ियाघर से जानवरों की एक सूची प्राप्त कर रहे हैं, और आप उन्हें संसाधित करने का इरादा रखते हैं। सभी जानवरों (आपके चिड़ियाघर में) का एक नाम, और एक अद्वितीय आईडी है। कुछ जानवर स्तनधारी हैं, कुछ सरीसृप हैं, कुछ उभयचर हैं, कुछ मछली आदि हैं, लेकिन वे सभी जानवर हैं।

इसलिए, आपकी जानवरों की सूची (जिसमें विभिन्न प्रकार के जानवर शामिल हैं) के साथ, आप कह सकते हैं कि सभी जानवरों का एक नाम है, इसलिए जाहिर है कि सभी जानवरों का नाम प्राप्त करना सुरक्षित होगा।

हालांकि, क्या होगा यदि आपके पास केवल मछलियों की सूची है, लेकिन उन्हें जानवरों की तरह व्यवहार करने की आवश्यकता है, क्या वह काम करता है? सहज रूप से, यह काम करना चाहिए, लेकिन C # 3.0 और इससे पहले, कोड का यह टुकड़ा संकलित नहीं करेगा:

IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>

इसका कारण यह है कि संकलक को यह नहीं पता "कि आप क्या चाहते हैं, या आप इसे पुनः प्राप्त करने के बाद जानवरों के संग्रह के साथ कर सकते हैं" । यह सब जानता है, IEnumerable<T>एक वस्तु को सूची में वापस लाने के माध्यम से एक रास्ता हो सकता है , और यह संभवतः आपको एक ऐसा जानवर डालने की अनुमति देगा जो मछली नहीं है, केवल मछली को शामिल करने वाले संग्रह में।

दूसरे शब्दों में, संकलक गारंटी नहीं दे सकता है कि यह अनुमति नहीं है:

animals.Add(new Mammal("Zebra"));

तो कंपाइलर सिर्फ आपके कोड को संकलित करने से इनकार करता है। यह सहसंयोजक है।

आइए कंट्रोवर्सी को देखें।

चूंकि हमारा चिड़ियाघर सभी जानवरों को संभाल सकता है, यह निश्चित रूप से मछली को संभाल सकता है, तो आइए हमारे चिड़ियाघर में कुछ मछलियों को जोड़ने की कोशिश करें।

C # 3.0 और इससे पहले, यह संकलित नहीं करता है:

List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>
fishes.Add(new Fish("Guppy"));

यहाँ, कंपाइलर इस कोड को अनुमति दे सकता है , भले ही यह विधि List<Animal>केवल इसलिए लौटे क्योंकि सभी मछलियाँ पशु हैं, इसलिए यदि हमने इसके प्रकार बदल दिए हैं:

List<Animal> fishes = GetAccessToFishes();
fishes.Add(new Fish("Guppy"));

तब यह काम करेगा, लेकिन संकलक यह निर्धारित नहीं कर सकता है कि आप ऐसा करने की कोशिश नहीं कर रहे हैं:

List<Fish> fishes = GetAccessToFishes(); // for some reason, returns List<Animal>
Fish firstFist = fishes[0];

चूंकि सूची वास्तव में जानवरों की सूची है, इसलिए इसे अनुमति नहीं है।

इसलिए, गर्भनिरोधक और सह-विचरण कैसे आप वस्तु संदर्भों का इलाज करते हैं और आपको उनके साथ क्या करने की अनुमति है।

inऔर outसी में कीवर्ड # 4.0 विशेष रूप से के निशान एक या अन्य रूप में इंटरफ़ेस। के साथ in, आपको जेनेरिक प्रकार (आमतौर पर टी) को इनपुट- एक्सपोजिशन में रखने की अनुमति दी जाती है , जिसका अर्थ है विधि तर्क और लेखन-केवल गुण।

इसके साथ out, आपको सामान्य प्रकार को आउटपुट- एक्सपोज़िशन में रखने की अनुमति है , जो कि विधि रिटर्न मान, रीड-ओनली प्रॉपर्टीज़, और मेथड पैरामीटर पैरामीटर है।

यह आपको वह करने की अनुमति देगा जो कोड के साथ करना है:

IEnumerable<Animal> animals = GetFishes(); // returns IEnumerable<Fish>
// since we can only get animals *out* of the collection, every fish is an animal
// so this is safe

List<T> टी पर दोनों में- और आउट-दिशाएं हैं, इसलिए यह न तो सह-संस्करण है और न ही गर्भ-संस्करण है, लेकिन एक इंटरफ़ेस जो आपको वस्तुओं को जोड़ने की अनुमति देता है, जैसे:

interface IWriteOnlyList<in T>
{
    void Add(T value);
}

आपको ऐसा करने की अनुमति देगा:

IWriteOnlyList<Fish> fishes = GetWriteAccessToAnimals(); // still returns
                                                            IWriteOnlyList<Animal>
fishes.Add(new Fish("Guppy")); <-- this is now safe

यहाँ कुछ वीडियो हैं जो अवधारणाओं को दिखाते हैं:

यहाँ एक उदाहरण है:

namespace SO2719954
{
    class Base { }
    class Descendant : Base { }

    interface IBibbleOut<out T> { }
    interface IBibbleIn<in T> { }

    class Program
    {
        static void Main(string[] args)
        {
            // We can do this since every Descendant is also a Base
            // and there is no chance we can put Base objects into
            // the returned object, since T is "out"
            // We can not, however, put Base objects into b, since all
            // Base objects might not be Descendant.
            IBibbleOut<Base> b = GetOutDescendant();

            // We can do this since every Descendant is also a Base
            // and we can now put Descendant objects into Base
            // We can not, however, retrieve Descendant objects out
            // of d, since all Base objects might not be Descendant
            IBibbleIn<Descendant> d = GetInBase();
        }

        static IBibbleOut<Descendant> GetOutDescendant()
        {
            return null;
        }

        static IBibbleIn<Base> GetInBase()
        {
            return null;
        }
    }
}

इन चिह्नों के बिना, निम्नलिखित संकलन कर सकता है:

public List<Descendant> GetDescendants() ...
List<Base> bases = GetDescendants();
bases.Add(new Base()); <-- uh-oh, we try to add a Base to a Descendant

या यह:

public List<Base> GetBases() ...
List<Descendant> descendants = GetBases(); <-- uh-oh, we try to treat all Bases
                                               as Descendants

हम्म, क्या आप कोविरेंस और कंट्रोवर्सी के लक्ष्य की व्याख्या कर पाएंगे? यह मुझे इसे और समझने में मदद कर सकता है।
निब्लीपिग

1
अंतिम बिट देखें, जो संकलक पहले रोका गया है, अंदर और बाहर का उद्देश्य यह कहना है कि आप इंटरफेस (या प्रकार) के साथ क्या कर सकते हैं जो सुरक्षित है, ताकि संकलक आपको सुरक्षित चीजें करने से रोक न सके। ।
लास वी। कार्लसन

शानदार जवाब, मैंने उन वीडियो को देखा जो वे बहुत उपयोगी थे, और आपके उदाहरण के साथ संयुक्त होकर मैंने इसे अभी सॉर्ट किया है। केवल एक ही प्रश्न शेष है, और वह यह है कि 'आउट' और 'इन' आवश्यक हैं, क्यों दृश्य स्टूडियो स्वचालित रूप से नहीं जानता कि आप क्या करने की कोशिश कर रहे हैं (या इसके पीछे क्या कारण है)?
NIBlyPig

स्वचालित रूप से "मैं देखता हूं कि आप वहां क्या करने की कोशिश कर रहे हैं" आमतौर पर कक्षाओं की तरह सामान की घोषणा करने पर आता है, यह प्रोग्रामर के प्रकारों को स्पष्ट रूप से चिह्नित करना बेहतर होता है। आप एक वर्ग में "इन" जोड़ने का प्रयास कर सकते हैं जिसमें टी और रिटर्न संकलक के तरीके हैं जो शिकायत करेंगे। कल्पना कीजिए कि अगर यह चुपचाप "केवल" हटा दिया जाए तो क्या होगा, यह पहले आपके लिए स्वचालित रूप से जोड़ा गया था।
लेज़स वी। कर्लसेन

1
यदि किसी कीवर्ड को इस लंबे स्पष्टीकरण की आवश्यकता है, तो कुछ स्पष्ट रूप से गलत है। मेरी राय में, C # इस विशेष मामले में बहुत स्मार्ट होने की कोशिश कर रहा है। बहरहाल, शांत व्याख्या के लिए धन्यवाद।
rr-

7

यह पोस्ट इस विषय पर पढ़ी गई सर्वश्रेष्ठ है

संक्षेप में, कोविरियन / कंट्रोवर्सी / इनवेरियन स्वचालित प्रकार की कास्टिंग (आधार से व्युत्पन्न और इसके विपरीत) से संबंधित है। उन प्रकार की जातियाँ केवल तभी संभव हैं जब कुछ गारंटीकृत वस्तुओं पर किए गए कार्यों को पढ़ने / लिखने के संदर्भ में सम्मानित किया जाता है। अधिक विवरण के लिए पोस्ट पढ़ें।


5
लिंक मृत दिखाई देता है। यहाँ एक संग्रहीत संस्करण है: web.archive.org/web/20140626123445/http://adamnathan.co.uk/…
si618

1
मुझे यह स्पष्टीकरण और भी पसंद है: codepureandsimple.com/…
volkit
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.