2012-10-11 10 views
10

मैं एक बाहरी वर्गआईडीवी <,> contravariance?

public static void DoStuffWithAnimals(IDictionary<string, Animal> animals) 

मेरी बुला कोड में निम्न विधि है, मैं पहले से ही एक Dictionary<string, Lion> वस्तु है, लेकिन मैं इस विधि के तर्क के रूप में इस में पारित नहीं हो सकता। तो IDictionary<,> contravariant नहीं है? मुझे कोई कारण नहीं दिख रहा है कि यह क्यों काम नहीं करना चाहिए।

एकमात्र समाधान मैं के बारे में सोच सकता है:

var animals = new Dictionary<string, Animal>(); 

foreach(var kvp in lions) { 
    animals.Add(kvp.Key, kvp.Value); 
} 

वहाँ ही वस्तुओं की एक नई शब्दकोश बनाए बिना, इस विधि में इस शब्दकोश पारित करने के लिए कोई रास्ता नहीं है?


संपादित करें:

यह मेरी विधि है के रूप में, मुझे पता है कि केवल सदस्य मैं शब्दकोश से उपयोग कर रहा हूँ TValue this[TKey key] की गेटर, जो IDictionary<TKey, TValue> का एक सदस्य है इस परिदृश्य में ऐसा है, तो है , मैं पैरामीटर के लिए 'व्यापक' प्रकार का उपयोग करने में असमर्थ हूं।

+2

IDictionary अपरिवर्तनीय हो रहा है। इसकी घोषणा में विवरण/आउट शामिल नहीं है। –

उत्तर

5

समाधान बुद्धिमान आप कुछ इस तरह, बस के बजाय एक्सेसर में पारित कर सकता है:

public static void DoStuffWithAnimals(Func<string, Animal> getAnimal) 
    { 
    } 

    var dicLions = new Dictionary<string, Lion>(); 
    DoStuffWithAnimals(s => dicLions[s]); 

जाहिर है कि अपनी आवश्यकताओं के लिए एक सा सरल होने की संभावना है, लेकिन यदि आपको केवल कुछ शब्दकोश विधियों की आवश्यकता है तो इसे जगह में लेना बहुत आसान है।

public class Accessor<T> : IAnimalAccessor where T : Animal 
    { 
     private readonly Dictionary<string, T> _dict; 

     public Accessor(Dictionary<string, T> dict) 
     { 
      _dict = dict; 
     } 

     public Animal GetItem(String key) 
     { 
      return _dict[key]; 
     } 
    } 

    public interface IAnimalAccessor 
    { 
     Animal GetItem(string key); 
    } 

    public static void DoStuffWithAnimals(IAnimalAccessor getAnimal) 
    { 
    } 

    var dicLions = new Dictionary<string, Lion>(); 
    var accessor = new Accessor<Lion>(dicLions); 
    DoStuffWithAnimals(accessor); 
+0

वह पहला मामला वास्तव में मेरे मामले के लिए ठीक है, यही वही है जो मुझे चाहिए :) @ लॉरेंस का जवाब भी मेरे मामले में भी काम करेगा। – Connell

4

मान लीजिए कि DerivedBase का उप प्रकार है। फिर, Dictionary<Base>Dictionary<Derived> की एक उप-प्रकार क्योंकि आप प्रकार Base की किसी भी वस्तु एक Dictionary<Derived> में नहीं डाल सकते हैं, लेकिन Dictionary<Derived>Dictionary<Base> की एक उप-प्रकार क्योंकि अगर आप एक Dictionary<Base> से बाहर एक वस्तु प्राप्त नहीं किया जा सकता नहीं किया जा सकता है, यह नहीं हो सकता है Derived बनें। इसलिए, Dictionary न तो इसके प्रकार पैरामीटर में सह-न ही contravariant है।

आम तौर पर, संग्रह जो आप लिख सकते हैं इस कारण से इनवेरिएंट हैं। यदि आपके पास एक अपरिवर्तनीय संग्रह है, तो यह हो सकता है। (यदि आपके पास कुछ प्रकार के लेखन-संग्रह संग्रह थे, तो यह contravariant हो सकता है।)

संपादित करें: और यदि आपके पास "संग्रह" था कि आप न तो डेटा प्राप्त कर सकते हैं और न ही डेटा डाल सकते हैं, तो यह दोनों सह- और contravariant। (यह भी शायद बेकार हो सकता है।)

+2

उदाहरण के लिए, 'DoStuffWithAnimals' को 'जानवरों' के लिए 'पिल्ला' जोड़ने की अनुमति है, लेकिन यदि जानवर 'शेर' का संग्रह है (जब तक शेर भूखे न हों) तो यह अवैध होना चाहिए। – Brian

+0

आह हाँ, संभोग! तो मेरे मामले में, यह केवल पढ़ने के लिए है और विधि मेरे शेरों के साथ किसी भी पिल्ले को नहीं जोड़ती है। इसका उपयोग करने वाला एकमात्र सदस्य 'टीवीएयू' [टीकेई कुंजी] 'का प्राप्तकर्ता है (जो' आईडीकेर <टीकेई, टीवीएयूयू 'का सदस्य है। क्या अभी भी कोई कामकाज नहीं है? – Connell

2

आप एक सामान्य बाधा जोड़ने के लिए public static void DoStuffWithAnimals(IDictionary<string, Animal> animals) को संशोधित कर सकते हैं:

यह एक और तरीका है कि आप अपने पशुओं के बीच कोड फिर से उपयोग का एक सा देता है। यहाँ कुछ है कि LINQPad में मेरे लिए काम करता है:

void Main() 
{ 
    var lions = new Dictionary<string, Lion>(); 
    lions.Add("one", new Lion{Name="Ben"}); 
    AnimalManipulator.DoStuffWithAnimals(lions); 
} 

public class AnimalManipulator 
{ 
    public static void DoStuffWithAnimals<T>(IDictionary<string, T> animals) 
    where T : Animal 
    { 
     foreach (var kvp in animals) 
     { 
      kvp.Value.MakeNoise(); 
     } 
    } 
} 

public class Animal 
{ 
    public string Name {get;set;} 
    public virtual string MakeNoise() 
    { 
     return "?"; 
    } 
} 

public class Lion : Animal 
{ 
    public override string MakeNoise() 
    { 
     return "Roar"; 
    } 
} 
+0

शानदार विचार! उसके लिए धन्यवाद। +1 – Connell

0

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

तो, आप अपनी कार्यक्षमता प्राप्त करने के लिए स्वयं इंटरफ़ेस बना सकते हैं और डिक्शनरी कक्षा का विस्तार कर सकते हैं।

3

सबसे पहले, सी # में कॉन्वर्सिस और contravariance केवल इंटरफेस और प्रतिनिधियों पर लागू होते हैं।

तो आपका प्रश्न वास्तव में IDictionary<TKey,TValue> है।

इस तरह से, यह याद रखना सबसे आसान है कि एक इंटरफ़ेस केवल सह/कॉन्ट्रैक्ट-वेरिएंट हो सकता है यदि किसी प्रकार के पैरामीटर के सभी मान या तो पास हो जाते हैं, या केवल पास हो जाते हैं।

उदाहरण के लिए (सहप्रसरण):

interface IReceiver<in T> // note 'in' modifier 
{ 
    void Add(T item); 
    void Remove(T item); 
} 

और (contravariance):

interface IGiver<out T> // note 'out' modifier 
{ 
    T Get(int index); 
    T RemoveAt(int index); 
} 

मैं इनमें से जो याद नहीं कर सकते हैं covariant है और जो contravariant है, लेकिन अंततः यह कोई बात नहीं , जब तक आप इसे/बाहर भेद में याद करते हैं।

IDictionary<TKey,TValue> के मामले में, दोनों प्रकार के पैरामीटर एक इन और आउट क्षमता दोनों में उपयोग किए जाते हैं, जिसका अर्थ है कि इंटरफ़ेस कॉन्वर्सेंट या contravariant नहीं हो सकता है। यह invariant है।

हालांकि, Dictionary<TKey,TValue> कक्षा IEnumerable<T> लागू करती है जो कि कॉन्वर्सेंट है।

+0

यह इस मामले में प्रासंगिक नहीं है, लेकिन सी # में सह-और contravariance भी प्रतिनिधियों पर लागू होता है। – svick

+0

चीयर्स @ एसविक, मैं जवाब अपडेट करूंगा। –

+0

IReadOnlyDictionary तब contravariant हो सकता है? – Slugart

2
  1. मुझे विश्वास है कि आप जो चाहते हैं उसे कॉन्वर्सिस कहा जाता है, contravariance नहीं।
  2. नेट में कक्षाएं सह-या contravariant नहीं हो सकती हैं, केवल इंटरफेस और प्रतिनिधि ही कर सकते हैं।
  3. IDictionary<TKey, TValue>, covariant नहीं किया जा सकता क्योंकि यह है कि आप की तरह कुछ करने के लिए अनुमति होगी:

    IDictionary<string, Sheep> sheep = new Dictionary<string, Sheep>(); 
    IDictionary<string, Animal> animals = sheep; 
    animals.Add("another innocent sheep", new Wolf()); 
    
  4. वहाँ नेट 4.5 में IReadOnlyDictionary<TKey, TValue> है। पहली नजर में यह कॉन्वेंटेंट हो सकता है (अन्य नया इंटरफ़ेस, IReadOnlyList<T> कॉन्वर्सेंट है)। दुर्भाग्यवश, ऐसा नहीं है, क्योंकि यह IEnumerable<KeyValuePair<TKey, TValue>> और KeyValuePair<TKey, TValue> लागू नहीं करता है।

  5. इस को हल करने के लिए, आप अपने विधि प्रकार पैरामीटर की एक बाधा के साथ सामान्य बना सकता है:

    void DoStuffWithAnimals<T>(IDictionary<string, T> animals) where T : Animal 
    
+0

** 1। ** Contravariance संकुचित से व्यापक रूप से परिवर्तित हो रहा है। क्या वह नहीं है जो मैं करने की कोशिश कर रहा हूं? ** 2। ** व्हाउप्स। मुझे पता था कि, मैंने उस हिस्से को पूछा था जब मैंने उस प्रश्न से पूछा 'IDictionary' को संदर्भित करने के लिए प्रश्न बदल दिया है। ** 3। ** आह, हाँ। यही वह है जो मैंने अन्य उत्तरों से सीखा है। ** 4। ** बगजर। – Connell

+0

पुन 1., यह इतना आसान नहीं है और मुझे लगता है कि विकिपीडिया में स्पष्टीकरण भ्रमित है। [एरिक लिपर्ट का उद्धरण:] (http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx) "एक पर विचार करें" ऑपरेशन "जो प्रकारों का उपयोग करता है। यदि किसी भी 'टी' और 'यू' पर लागू ऑपरेशन के परिणाम हमेशा 'टी' और' यू 'के समान रिश्ते के साथ दो प्रकार के' टी 'और' यू 'में होते हैं, तो ऑपरेशन कहा जाता है "covariant"। यदि ऑपरेशन अपने परिणामों पर bigness और छोटीता को उलट देता है [...] तो ऑपरेशन "contravariant" कहा जाता है। " – svick

संबंधित मुद्दे