विरासत वंशानुक्रम का उपयोग करते हुए यहां एक सरल उदाहरण दिया गया है।
साधारण वर्ग पदानुक्रम को देखते हुए:
और कोड में:
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
फ़ंक्शन के खिलाफ लागू किया जाता है (पैरामीटर के रूप में पारित), हालांकि फ़ंक्शन स्वयं एक कम व्युत्पन्न प्रकार का उपयोग करता है।