Covariance और contravariance वास्तविक दुनिया उदाहरण है


162

मुझे यह समझने में थोड़ी परेशानी हो रही है कि मैं वास्तविक दुनिया में कोवरियन और कंट्रोवर्सी का उपयोग कैसे करूंगा।

अब तक, मैंने जो एकमात्र उदाहरण देखे हैं, वही पुराने सरणी उदाहरण हैं।

object[] objectArray = new string[] { "string 1", "string 2" };

एक उदाहरण देखना अच्छा होगा जो मुझे अपने विकास के दौरान इसका उपयोग करने की अनुमति देगा यदि मैं इसे कहीं और इस्तेमाल कर सकता हूं।


1
मैं (मेरे अपने) प्रश्न के उत्तर में सहसंयोजक का पता लगाता हूं: सहसंयोजक प्रकार: उदाहरण के लिए । मुझे लगता है कि आप इसे दिलचस्प, और उम्मीद से शिक्षाप्रद पाएंगे।
क्रिस्टियन डियाकोन्सु

जवाबों:


109

मान लीजिए कि आपके पास एक क्लास पर्सन और एक क्लास है, जो इसे प्राप्त करता है, टीचर। आपके पास कुछ ऑपरेशन हैं जो IEnumerable<Person>तर्क के रूप में लेते हैं । आपके स्कूल की कक्षा में आपके पास एक तरीका है जो एक रिटर्न देता है IEnumerable<Teacher>। Covariance आपको उन तरीकों के लिए सीधे परिणाम का उपयोग करने की अनुमति देता है जो IEnumerable<Person>एक कम व्युत्पन्न (अधिक सामान्य) प्रकार के लिए अधिक व्युत्पन्न प्रकार को प्रतिस्थापित करते हैं। विपरीत, सहज ज्ञान युक्त, आपको अधिक सामान्य प्रकार का उपयोग करने की अनुमति देता है, जहां एक अधिक व्युत्पन्न प्रकार निर्दिष्ट है।

MSDN पर जेनरिक में कोवरियन और कंट्रावेरियन भी देखें ।

कक्षाएं :

public class Person 
{
     public string Name { get; set; }
} 

public class Teacher : Person { } 

public class MailingList
{
    public void Add(IEnumerable<out Person> people) { ... }
}

public class School
{
    public IEnumerable<Teacher> GetTeachers() { ... }
}

public class PersonNameComparer : IComparer<Person>
{
    public int Compare(Person a, Person b) 
    { 
        if (a == null) return b == null ? 0 : -1;
        return b == null ? 1 : Compare(a,b);
    }

    private int Compare(string a, string b)
    {
        if (a == null) return b == null ? 0 : -1;
        return b == null ? 1 : a.CompareTo(b);
    }
}

उपयोग :

var teachers = school.GetTeachers();
var mailingList = new MailingList();

// Add() is covariant, we can use a more derived type
mailingList.Add(teachers);

// the Set<T> constructor uses a contravariant interface, IComparer<in T>,
// we can use a more generic type than required.
// See https://msdn.microsoft.com/en-us/library/8ehhxeaf.aspx for declaration syntax
var teacherSet = new SortedSet<Teachers>(teachers, new PersonNameComparer());

14
@FilipBartuzi - अगर, मेरी तरह जब मैंने यह जवाब लिखा था, तो आप एक ऐसे विश्वविद्यालय में कार्यरत थे जो बहुत ही वास्तविक उदाहरण है।
tvanfosson

5
यह उत्तर कैसे चिह्नित किया जा सकता है जब यह सवाल का जवाब नहीं देता है और सह # संचलन संस्करण # का उपयोग करने का कोई उदाहरण नहीं देता है?
बरकक्फ

@barakcaf ने विरोधाभासी का एक उदाहरण जोड़ा। सुनिश्चित नहीं हैं कि आप कोविर्सियस का उदाहरण क्यों नहीं देख रहे थे - शायद आपको कोड को नीचे स्क्रॉल करने की आवश्यकता थी - लेकिन मैंने उसके आस-पास कुछ टिप्पणियां जोड़ दीं।
तवानफोसन

@tvanfosson कोड सह / गर्भनिरोधक का उपयोग करता है, मुझे लगता है कि यह नहीं बताता कि इसे कैसे घोषित किया जाए। उदाहरण जेनेरिक घोषणा में / बाहर का उपयोग नहीं दिखाता है जबकि दूसरा जवाब देता है।
बरकाकफ

इसलिए, अगर मुझे यह सही लगता है, तो सहसंयोजक वह है जो सी # में लिस्कोव के प्रतिस्थापन सिद्धांत की अनुमति देता है, क्या यह सही है?
मिगुएल वेलोसो

136
// Contravariance
interface IGobbler<in T> {
    void gobble(T t);
}

// Since a QuadrupedGobbler can gobble any four-footed
// creature, it is OK to treat it as a donkey gobbler.
IGobbler<Donkey> dg = new QuadrupedGobbler();
dg.gobble(MyDonkey());

// Covariance
interface ISpewer<out T> {
    T spew();
}

// A MouseSpewer obviously spews rodents (all mice are
// rodents), so we can treat it as a rodent spewer.
ISpewer<Rodent> rs = new MouseSpewer();
Rodent r = rs.spew();

संपूर्णता के लिए…

// Invariance
interface IHat<T> {
    void hide(T t);
    T pull();
}

// A RabbitHat…
IHat<Rabbit> rHat = RabbitHat();

// …cannot be treated covariantly as a mammal hat…
IHat<Mammal> mHat = rHat;      // Compiler error
// …because…
mHat.hide(new Dolphin());      // Hide a dolphin in a rabbit hat??

// It also cannot be treated contravariantly as a cottontail hat…
IHat<CottonTail> cHat = rHat;  // Compiler error
// …because…
rHat.hide(new MarshRabbit());
cHat.pull();                   // Pull a marsh rabbit out of a cottontail hat??

138
मुझे यह यथार्थवादी उदाहरण पसंद है। मैं पिछले हफ्ते सिर्फ कुछ गदगद कोड लिख रहा था और मैं बहुत खुश था कि अब हमारे पास सहसंयोजक है। :-)
एरिक लिपर्ट

4
@ जेवदबा के साथ यह टिप्पणी एरिकलिपर्ट को बता रही है कि सहसंयोजक क्या है और विरोधाभासी मेरे लिए एक यथार्थवादी सहसंयोजक उदाहरण है जो मेरी दादी को बता रहा है कि अंडे कैसे चूसना है! : p
iAteABug_And_iLiked_it

1
सवाल यह नहीं पूछा गया कि कॉन्ट्रोवर्सी और कॉवेरियन क्या कर सकते हैं , इसने पूछा कि आपको इसका उपयोग करने की आवश्यकता क्यों होगी । आपका उदाहरण व्यावहारिक से बहुत दूर है क्योंकि इसकी आवश्यकता नहीं है। मैं एक QuadrupedGobbler बना सकता हूं और इसे स्वयं के रूप में मान सकता हूं (इसे IGobbler <Quadruped> पर असाइन करें) और यह अभी भी गदहे को जगा सकता है (मैं एक गदहे को गोब्बल विधि में पास कर सकता हूं जिसके लिए एक चौपाया की आवश्यकता होती है)। कोई कंट्रोवर्सी नहीं चाहिए। यही कारण है कि के शांत है कि हम कर सकते हैं एक DonkeyGobbler के रूप में एक QuadrupedGobbler का इलाज है, लेकिन कारण है कि हम इस मामले में, की जरूरत है एक QuadrupedGobbler पहले से ही गदहे अप हडप कर सकते हैं?
वायर्ड_इन

1
@wired_in क्योंकि जब आप केवल गधों की परवाह करते हैं, तो सामान्य होने के नाते रास्ते में मिल सकते हैं। उदाहरण के लिए, यदि आपके पास एक खेत है जो गदहे की आपूर्ति करता है, तो आप इसे व्यक्त कर सकते हैं void feed(IGobbler<Donkey> dg)। यदि आप एक IGobbler <Quadruped> को एक पैरामीटर के रूप में लेते हैं, तो इसके बजाय, आप ऐसे ड्रैगन में पास नहीं हो सकते जो केवल गधों को खाता है।
मार्सेलो कैंटोस

1
पार्टी के लिए देर हो रही है, लेकिन यह सबसे अच्छा लिखित उदाहरण है जो मैंने एसओ के आसपास देखा है। हास्यास्पद होने के दौरान पूर्ण समझ में आता है। मैं जवाब के साथ अपने खेल को बढ़ाने जा रहा हूँ ...
जेसी विलियम्स

121

यहाँ पर मैंने एक साथ रखा है जिससे मुझे अंतर समझने में मदद मिले

public interface ICovariant<out T> { }
public interface IContravariant<in T> { }

public class Covariant<T> : ICovariant<T> { }
public class Contravariant<T> : IContravariant<T> { }

public class Fruit { }
public class Apple : Fruit { }

public class TheInsAndOuts
{
    public void Covariance()
    {
        ICovariant<Fruit> fruit = new Covariant<Fruit>();
        ICovariant<Apple> apple = new Covariant<Apple>();

        Covariant(fruit);
        Covariant(apple); //apple is being upcasted to fruit, without the out keyword this will not compile
    }

    public void Contravariance()
    {
        IContravariant<Fruit> fruit = new Contravariant<Fruit>();
        IContravariant<Apple> apple = new Contravariant<Apple>();

        Contravariant(fruit); //fruit is being downcasted to apple, without the in keyword this will not compile
        Contravariant(apple);
    }

    public void Covariant(ICovariant<Fruit> fruit) { }

    public void Contravariant(IContravariant<Apple> apple) { }
}

tldr

ICovariant<Fruit> apple = new Covariant<Apple>(); //because it's covariant
IContravariant<Apple> fruit = new Contravariant<Fruit>(); //because it's contravariant

10
यह सबसे अच्छी बात है जो मैंने अब तक देखी है जो स्पष्ट और संक्षिप्त है। उदाहरण के लिए!
रोब एल

6
फलों को सेब ( Contravarianceउदाहरण के लिए) में कैसे उतारा जा सकता Fruitहै Apple?
टोबियास मार्शेल

@TobiasMarschall का अर्थ है कि आपको "बहुरूपता" पर अधिक अध्ययन करना होगा
snr

56

इन-आउट कीवर्ड जेनरेटर मापदंडों के साथ इंटरफेस और डेलिगेट्स के लिए संकलक के कास्टिंग नियमों को नियंत्रित करते हैं:

interface IInvariant<T> {
    // This interface can not be implicitly cast AT ALL
    // Used for non-readonly collections
    IList<T> GetList { get; }
    // Used when T is used as both argument *and* return type
    T Method(T argument);
}//interface

interface ICovariant<out T> {
    // This interface can be implicitly cast to LESS DERIVED (upcasting)
    // Used for readonly collections
    IEnumerable<T> GetList { get; }
    // Used when T is used as return type
    T Method();
}//interface

interface IContravariant<in T> {
    // This interface can be implicitly cast to MORE DERIVED (downcasting)
    // Usually means T is used as argument
    void Method(T argument);
}//interface

class Casting {

    IInvariant<Animal> invariantAnimal;
    ICovariant<Animal> covariantAnimal;
    IContravariant<Animal> contravariantAnimal;

    IInvariant<Fish> invariantFish;
    ICovariant<Fish> covariantFish;
    IContravariant<Fish> contravariantFish;

    public void Go() {

        // NOT ALLOWED invariants do *not* allow implicit casting:
        invariantAnimal = invariantFish; 
        invariantFish = invariantAnimal; // NOT ALLOWED

        // ALLOWED covariants *allow* implicit upcasting:
        covariantAnimal = covariantFish; 
        // NOT ALLOWED covariants do *not* allow implicit downcasting:
        covariantFish = covariantAnimal; 

        // NOT ALLOWED contravariants do *not* allow implicit upcasting:
        contravariantAnimal = contravariantFish; 
        // ALLOWED contravariants *allow* implicit downcasting
        contravariantFish = contravariantAnimal; 

    }//method

}//class

// .NET Framework Examples:
public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable { }
public interface IEnumerable<out T> : IEnumerable { }


class Delegates {

    // When T is used as both "in" (argument) and "out" (return value)
    delegate T Invariant<T>(T argument);

    // When T is used as "out" (return value) only
    delegate T Covariant<out T>();

    // When T is used as "in" (argument) only
    delegate void Contravariant<in T>(T argument);

    // Confusing
    delegate T CovariantBoth<out T>(T argument);

    // Confusing
    delegate T ContravariantBoth<in T>(T argument);

    // From .NET Framework:
    public delegate void Action<in T>(T obj);
    public delegate TResult Func<in T, out TResult>(T arg);

}//class

मछली को मान लेना पशु का एक उपप्रकार है। वैसे शानदार जवाब।
राजन प्रसाद

48

विरासत वंशानुक्रम का उपयोग करते हुए यहां एक सरल उदाहरण दिया गया है।

साधारण वर्ग पदानुक्रम को देखते हुए:

यहां छवि विवरण दर्ज करें

और कोड में:

public abstract class LifeForm  { }
public abstract class Animal : LifeForm { }
public class Giraffe : Animal { }
public class Zebra : Animal { }

Invariance (यानी सामान्य प्रकार के मापदंडों * नहीं * के साथ सजाया inया outकीवर्ड)

लगता है, इस तरह के रूप में एक विधि

public static void PrintLifeForms(IList<LifeForm> lifeForms)
{
    foreach (var lifeForm in lifeForms)
    {
        Console.WriteLine(lifeForm.GetType().ToString());
    }
}

... एक विषम संग्रह को स्वीकार करना चाहिए: (जो यह करता है)

var myAnimals = new List<LifeForm>
{
    new Giraffe(),
    new Zebra()
};
PrintLifeForms(myAnimals); // Giraffe, Zebra

हालाँकि, अधिक व्युत्पन्न प्रकार का संग्रह पास करना विफल रहता है!

var myGiraffes = new List<Giraffe>
{
    new Giraffe(), // "Jerry"
    new Giraffe() // "Melman"
};
PrintLifeForms(myGiraffes); // Compile Error!

cannot convert from 'System.Collections.Generic.List<Giraffe>' to 'System.Collections.Generic.IList<LifeForm>'

क्यों? क्योंकि सामान्य पैरामीटर IList<LifeForm>सहसंयोजक नहीं है - IList<T>अपरिवर्तनीय है, इसलिए IList<LifeForm>केवल संग्रह को स्वीकार करता है (जो IList लागू करता है) जहां पैरामीटर प्रकार Tहोना चाहिए LifeForm

यदि विधि का कार्यान्वयन PrintLifeFormsदुर्भावनापूर्ण था (लेकिन एक ही विधि हस्ताक्षर है), तो संकलक को रोकने का कारण List<Giraffe>स्पष्ट हो जाता है:

 public static void PrintLifeForms(IList<LifeForm> lifeForms)
 {
     lifeForms.Add(new Zebra());
 }

चूंकि IListतत्वों को जोड़ने या हटाने की अनुमति है, LifeFormइसलिए इस प्रकार के किसी भी उपवर्ग को पैरामीटर में जोड़ा जा सकता है lifeForms, और विधि के लिए पारित व्युत्पन्न प्रकारों के किसी भी संग्रह के प्रकार का उल्लंघन होगा। (यहाँ, दुर्भावनापूर्ण विधि एक जोड़ने का प्रयास करेंगे Zebraकरने के लिए var myGiraffes)। सौभाग्य से, कंपाइलर हमें इस खतरे से बचाता है।

कोवरियनस (जेनेरिक प्रकार के साथ सजाया गया out)

सहसंयोजक व्यापक रूप से अपरिवर्तनीय संग्रह के साथ प्रयोग किया जाता है (अर्थात जहां नए तत्वों को जोड़ा नहीं जा सकता है या संग्रह से हटाया जा सकता है)

ऊपर दिए गए उदाहरण का समाधान यह सुनिश्चित करना है कि एक सहसंयोजक सामान्य संग्रह प्रकार का उपयोग किया जाता है, उदाहरण के लिए IEnumerable(के रूप में परिभाषित IEnumerable<out T>)। IEnumerableसंग्रह में परिवर्तन करने के लिए कोई विधि नहीं है, और outसहसंयोजक के परिणामस्वरूप , उपप्रकार के साथ किसी भी संग्रह LifeFormको अब विधि में पारित किया जा सकता है:

public static void PrintLifeForms(IEnumerable<LifeForm> lifeForms)
{
    foreach (var lifeForm in lifeForms)
    {
        Console.WriteLine(lifeForm.GetType().ToString());
    }
}

PrintLifeFormsअब के साथ बुलाया जा सकता है Zebras, Giraffesऔर किसी IEnumerable<>भी उपवर्ग काLifeForm

कंट्रोवर्सी (जेनेरिक प्रकार के साथ सजाया गया in)

जब कार्यों को मापदंडों के रूप में पारित किया जाता है तो कंट्रावेरियन का उपयोग अक्सर किया जाता है।

यहाँ एक फ़ंक्शन का एक उदाहरण है, जो Action<Zebra>एक पैरामीटर के रूप में लेता है , और एक ज़ेबरा के ज्ञात उदाहरण पर इसे आमंत्रित करता है:

public void PerformZebraAction(Action<Zebra> zebraAction)
{
    var zebra = new Zebra();
    zebraAction(zebra);
}

जैसा कि अपेक्षित था, यह ठीक काम करता है:

var myAction = new Action<Zebra>(z => Console.WriteLine("I'm a zebra"));
PerformZebraAction(myAction); // I'm a zebra

सहज रूप से, यह विफल हो जाएगा:

var myAction = new Action<Giraffe>(g => Console.WriteLine("I'm a giraffe"));
PerformZebraAction(myAction); 

cannot convert from 'System.Action<Giraffe>' to 'System.Action<Zebra>'

हालाँकि, यह सफल होता है

var myAction = new Action<Animal>(a => Console.WriteLine("I'm an animal"));
PerformZebraAction(myAction); // I'm an animal

और यहां तक ​​कि यह भी सफल होता है:

var myAction = new Action<object>(a => Console.WriteLine("I'm an amoeba"));
PerformZebraAction(myAction); // I'm an amoeba

क्यों? क्योंकि Actionके रूप में परिभाषित किया गया है Action<in T>, यानी यह है contravariant, जिसका अर्थ है कि Action<Zebra> myAction, myAction"सबसे" ए पर हो सकता है Action<Zebra>, लेकिन कम व्युत्पन्न सुपरक्लास Zebraभी स्वीकार्य हैं।

हालांकि यह पहली बार में गैर-सहज हो सकता है (जैसे कि Action<object>एक पैरामीटर की आवश्यकता के रूप में कैसे पारित किया जा सकता है Action<Zebra>?), यदि आप चरणों को अनपैक करते हैं, तो आप ध्यान देंगे कि कॉलिंग फ़ंक्शन ( PerformZebraAction) इस मामले में एक Zebraउदाहरण के लिए स्वयं जिम्मेदार है। ) फ़ंक्शन के लिए - डेटा कॉलिंग कोड से नहीं आता है।

इस तरीके से उच्च क्रम के कार्यों का उपयोग करने के उल्टे दृष्टिकोण के कारण, जब तक Actionइसे लागू किया जाता है, यह अधिक व्युत्पन्न Zebraउदाहरण है जिसे zebraActionफ़ंक्शन के खिलाफ लागू किया जाता है (पैरामीटर के रूप में पारित), हालांकि फ़ंक्शन स्वयं एक कम व्युत्पन्न प्रकार का उपयोग करता है।


7
यह विभिन्न विचरण विकल्पों के लिए एक शानदार व्याख्या है, क्योंकि यह उदाहरण के माध्यम से बात करता है और यह भी स्पष्ट करता है कि संकलक बिना प्रतिबंधित या परमिट के कीवर्ड को
क्यों रोकते हैं

inकीवर्ड का उपयोग कहां किया जाता हैकंट्रोवर्सी के ?
जावदबा

@javadba ऊपर में, Action<in T>और Func<in T, out TResult>इनपुट प्रकार में contravariant हैं। (मेरे उदाहरण मौजूदा अपरिवर्तनीय (सूची), सहसंयोजक (IEnumerable) और contravariant (एक्शन,
फ़ंक

ठीक है मैं नहीं करता C# कि पता नहीं होगा।
जावदबा

यह स्कैला में काफी हद तक समान है, बस अलग-अलग वाक्यविन्यास - [+ T] टी में सहसंयोजक होगा, [-T] टी में contravariant होगा, Scala भी 'बाधा' के बीच 'लागू कर सकता है, और' कुछ भी नहीं उप-उपवर्ग, जो C # नहीं है
स्टुअर्टएलसी

32
class A {}
class B : A {}

public void SomeFunction()
{
    var someListOfB = new List<B>();
    someListOfB.Add(new B());
    someListOfB.Add(new B());
    someListOfB.Add(new B());
    SomeFunctionThatTakesA(someListOfB);
}

public void SomeFunctionThatTakesA(IEnumerable<A> input)
{
    // Before C# 4, you couldn't pass in List<B>:
    // cannot convert from
    // 'System.Collections.Generic.List<ConsoleApplication1.B>' to
    // 'System.Collections.Generic.IEnumerable<ConsoleApplication1.A>'
}

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

बस एक जाल के बारे में आपको चेतावनी देने के लिए:

var ListOfB = new List<B>();
if(ListOfB is IEnumerable<A>)
{
    // In C# 4, this branch will
    // execute...
    Console.Write("It is A");
}
else if (ListOfB is IEnumerable<B>)
{
    // ...but in C# 3 and earlier,
    // this one will execute instead.
    Console.Write("It is B");
}

यह वैसे भी भयानक कोड है, लेकिन यह मौजूद है और C # 4 में बदलते व्यवहार की वजह से इस तरह के एक निर्माण का उपयोग करने के लिए कीड़े खोजने के लिए सूक्ष्म और कठिन परिचय हो सकता है।


तो यह किसी भी चीज़ से अधिक संग्रह को प्रभावित करता है, क्योंकि c # 3 में आप अधिक व्युत्पन्न प्रकार को कम व्युत्पन्न प्रकार की विधि में पारित कर सकते हैं।
रेजर

3
हां, बड़ा बदलाव यह है कि IEnumerable अब इसका समर्थन करता है, जबकि यह पहले नहीं था।
माइकल स्टम

4

से MSDN

निम्न कोड उदाहरण विधि समूहों के लिए सहसंयोजक और विरोधाभासी समर्थन दिखाता है

static object GetObject() { return null; }
static void SetObject(object obj) { }

static string GetString() { return ""; }
static void SetString(string str) { }

static void Test()
{
    // Covariance. A delegate specifies a return type as object, 
    // but you can assign a method that returns a string.
    Func<object> del = GetString;

    // Contravariance. A delegate specifies a parameter type as string, 
    // but you can assign a method that takes an object.
    Action<string> del2 = SetObject;
}

4

contravariance

वास्तविक दुनिया में, आप हमेशा खरगोशों के लिए आश्रय के बजाय जानवरों के लिए एक आश्रय का उपयोग कर सकते हैं क्योंकि हर बार एक पशु आश्रय एक खरगोश को होस्ट करता है यह एक जानवर है। हालांकि, यदि आप एक पशु आश्रय के बजाय एक खरगोश आश्रय का उपयोग करते हैं तो उसके कर्मचारी एक बाघ द्वारा खाया जा सकता है।

कोड में, इसका मतलब है कि यदि आपके पास एक है IShelter<Animal> animalsतो आप बस लिख सकते हैं IShelter<Rabbit> rabbits = animals यदि आप वादा करते हैं और उपयोग करते Tहैं तो IShelter<T>केवल विधि पैरामीटर जैसे:

public class Contravariance
{
    public class Animal { }
    public class Rabbit : Animal { }

    public interface IShelter<in T>
    {
        void Host(T thing);
    }

    public void NoCompileErrors()
    {
        IShelter<Animal> animals = null;
        IShelter<Rabbit> rabbits = null;

        rabbits = animals;
    }
}

और एक आइटम को एक अधिक सामान्य के साथ बदलें, अर्थात विचरण को कम करें या गर्भनिरोधक विचरण को शुरू करें ।

सहप्रसरण

वास्तविक दुनिया में, आप हमेशा जानवरों के आपूर्तिकर्ता के बजाय खरगोशों के आपूर्तिकर्ता का उपयोग कर सकते हैं क्योंकि हर बार एक खरगोश आपूर्तिकर्ता आपको एक खरगोश देता है यह एक जानवर है। हालांकि, यदि आप खरगोश आपूर्तिकर्ता के बजाय एक पशु आपूर्तिकर्ता का उपयोग करते हैं तो आप बाघ द्वारा खाया जा सकता है।

कोड में, इसका मतलब है कि आप एक है, तो ISupply<Rabbit> rabbitsआप बस लिख सकते हैं ISupply<Animal> animals = rabbits , तो तुमसे वादा करता हूँ और उपयोग Tमें ISupply<T>केवल के रूप में विधि वापसी मान इतना चाहते:

public class Covariance
{
    public class Animal { }
    public class Rabbit : Animal { }

    public interface ISupply<out T>
    {
        T Get();
    }

    public void NoCompileErrors()
    {
        ISupply<Animal> animals = null;
        ISupply<Rabbit> rabbits = null;

        animals = rabbits;
    }
}

और एक आइटम को एक अधिक व्युत्पन्न के साथ बदलें, अर्थात विचरण को बढ़ाएं या सह विचरण का परिचय दें ।

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

आप देने के लिए चाहते हो सकता है यह एक यह चारों ओर अपने सिर को दोगुना-लपेट के लिए पढ़ें।


आप एक बाघ द्वारा खाया जा सकता है जो एक मूल्य के लायक था
javadba

आपकी टिप्पणी contravarianceरोचक है। मैं इसे एक परिचालन आवश्यकता के संकेत के रूप में पढ़ रहा हूं : कि अधिक सामान्य प्रकार से प्राप्त सभी प्रकार के उपयोग के मामलों का समर्थन करना चाहिए। तो इस मामले में पशु आश्रय हर पशु प्रकार को आश्रय देने में सक्षम होना चाहिए। उस मामले में एक नया उपवर्ग जोड़ने से सुपरक्लास टूट सकता है! वह है - यदि हम एक उपप्रकार टायरानोसोरस रेक्स जोड़ते हैं तो यह हमारे मौजूदा पशु आश्रय को नष्ट कर सकता है ।
जावदबा

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

3

कनवर्टर प्रतिनिधि मुझे एक साथ काम करने वाली दोनों अवधारणाओं की कल्पना करने में मदद करता है:

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