2010-08-25 17 views
9

में जेनेरिक का उपयोग करते समय बहुरूपता को समझने में मेरी सहायता करें मुझे जेनरिक्स का उपयोग करते समय पॉलिमॉर्फिज्म कैसे काम करता है यह समझने में समस्या आ रही है।कृपया C#

public interface IMyInterface 
{ 
    void MyMethod(); 
} 

public class MyClass : IMyInterface 
{ 
    public void MyMethod() 
    { 
    } 
} 

public class MyContainer<T> where T : IMyInterface 
{ 
    public IList<T> Contents; 
} 

मैं तो ऐसा कर सकते हैं, जो सिर्फ ठीक काम करता है: उदाहरण के लिए, मैं निम्नलिखित कार्यक्रम को परिभाषित किया है

MyContainer<MyClass> container = new MyContainer<MyClass>(); 
container.Contents.Add(new MyClass()); 

मैं कई कक्षाओं कि MyInterface को लागू किया है। मैं एक विधि है कि सभी MyContainer वस्तुओं स्वीकार कर सकते हैं लिखने के लिए करना चाहते हैं:

public void CallAllMethodsInContainer(MyContainer<IMyInterface> container) 
{ 
    foreach (IMyInterface myClass in container.Contents) 
    { 
     myClass.MyMethod(); 
    } 
} 

अब, मैं इस विधि कॉल करने के लिए करना चाहते हैं।

MyContainer<MyClass> container = new MyContainer<MyClass>(); 
container.Contents.Add(new MyClass()); 
this.CallAllMethodsInContainer(container); 

यह काम नहीं किया। निश्चित रूप से, क्योंकि MyClass IMyInterface लागू करता है, मुझे बस इसे कास्ट करने में सक्षम होना चाहिए?

MyContainer<IMyInterface> newContainer = (MyContainer<IMyInterface>)container; 

यह काम नहीं करता था। मैं निश्चित रूप से IMyInterface एक सामान्य MyClass डाली कर सकते हैं:

MyClass newClass = new MyClass(); 
IMyInterface myInterface = (IMyInterface)newClass; 

तो, कम से कम मैं पूरी तरह से है कि गलत समझा नहीं किया है। मुझे बिल्कुल यकीन नहीं है कि मैं एक ऐसी विधि कैसे लिखूं जो समान इंटरफ़ेस के अनुरूप कक्षाओं का सामान्य संग्रह स्वीकार करता हो।

यदि आवश्यकता हो तो मेरे पास इस समस्या के आसपास पूरी तरह से हैक करने की योजना है, लेकिन मैं वास्तव में इसे ठीक से करना पसंद करूंगा।

अग्रिम धन्यवाद।

+2

यह वह जगह है जहां लोग डरावनी शब्द जैसे कॉन्वर्सिस और contravariance बाहर trot। – Greg

+0

@ ग्रेग: प्लस साइड पर, मुझे लगता है कि इन अवधारणाओं की मेरी * स्वयं * समझ में हाल ही में इस तरह के प्रश्नों के कारण वास्तव में बाहर निकल गया है जो पॉप अप हो रहा है! –

+0

अवधारणाएं अच्छी हैं, लेकिन नाम डरावने हैं। :) – Greg

उत्तर

4

नोट: सभी मामलों में, आप एक ठोस वस्तु को लागू करने वाली IList<?>

जब आप सामान्य बाधा रखने के लिए Contents क्षेत्र को प्रारंभ करना होगा, तो आप कर सकते हैं:

public IList<T> Contents = new List<T>(); 

जब आप डॉन 'टी, तो आप कर सकते हैं:

public IList<MyInterface> Contents = new List<MyInterface>(); 

विधि 1:

बदलें करने के लिए विधि:

public void CallAllMethodsInContainer<T>(MyContainer<T> container) where T : IMyInterface 
{ 
    foreach (T myClass in container.Contents) 
    { 
     myClass.MyMethod(); 
    } 
} 

और करने के लिए टुकड़ा:

MyContainer<MyClass> container = new MyContainer<MyClass>(); 
container.Contents.Add(new MyClass()); 
this.CallAllMethodsInContainer(container); 

विधि 2:

वैकल्पिक रूप से, MyContainer<T> वर्ग इस तरह के CallAllMethodsInContainer विधि के लिए कदम:

public void CallAllMyMethodsInContents() 
    { 
     foreach (T myClass in Contents) 
     { 
      myClass.MyMethod(); 
     } 
    } 

और करने के लिए स्निपेट बदलना:

MyContainer<MyClass> container = new MyContainer<MyClass>(); 
container.Contents.Add(new MyClass()); 
container.CallAllMyMethodsInContents(); 

विधि 3:

संपादित करें: फिर भी एक और विकल्प इस तरह MyContainer वर्ग से सामान्य बाधा दूर करने के लिए है:

public class MyContainer 
{ 
    public IList<MyInterface> Contents; 
} 

और विधि हस्ताक्षर को

पर बदलने के लिए

तब स्निपेट के रूप में काम करना चाहिए:

MyContainer container = new MyContainer(); 
container.Contents.Add(new MyClass()); 
this.CallAllMethodsInContainer(container); 

ध्यान दें कि यह विकल्प के साथ, कंटेनर की Contents सूची वस्तुओं है कि MyInterface लागू के किसी भी संयोजन को स्वीकार करेंगे।

3

वाह बातें कर सकता है आप MyContainer<IMyInterface> करने के लिए MyContainer<MyClass> डाली नहीं कर सकता है, इस सवाल किया गया हाल ही में बहुत आ रहा है।

संक्षिप्त उत्तर: नहीं, यह संभव नहीं है।यहां बताया गया है संभव है:

public void CallAllMethodsInContainer<T>(MyContainer<T> container) where T : IMyInterface 
{ 
    foreach (IMyInterface myClass in container.Contents) 
    { 
     myClass.MyMethod(); 
    } 
} 

और यहाँ क्यों क्या आप की कोशिश की संभव (this recent answer of mine से लिया गया) नहीं है:

पर विचार करें List<T> प्रकार। मान लें कि आपके पास List<string> और List<object> है। स्ट्रिंग ऑब्जेक्ट से निकलती है, लेकिन यह List<string> का पालन नहीं करती है List<object> से निकलती है; अगर ऐसा हुआ, तो आप इस तरह कोड हो सकता है:

var strings = new List<string>(); 

// If this cast were possible... 
var objects = (List<object>)strings; 

// ...crap! then you could add a DateTime to a List<string>! 
objects.Add(new DateTime(2010, 8, 23));23)); 

उपरोक्त कोड को दिखाता है कि यह होने का क्या मतलब है (और होने के लिए नहीं) एक covariant type। ध्यान दें कि T<D> को किसी अन्य प्रकार T<B> पर DB से प्राप्त होता है (.NET 4.0 में) Tकॉन्वर्स है; एक सामान्य प्रकार covariant है यदि इसका सामान्य प्रकार तर्क केवल आउटपुट के रूप में प्रकट होता है - यानी, केवल-पढ़ने योग्य गुण और फ़ंक्शन रिटर्न मान।

इसके बारे में लगता है कि इस तरह से: कुछ प्रकार T<B> हमेशा एक B की आपूर्ति, तो हमेशा की आपूर्ति है कि एक D (T<D>) एक T<B> के रूप में संचालित करने के लिए के बाद से सभी D रों B रों हैं सक्षम हो जाएगा है।

संयोग से, एक प्रकार contravariant है यदि इसका सामान्य प्रकार पैरामीटर केवल इनपुट के रूप में प्रकट होता है - यानी, विधि पैरामीटर। यदि एक प्रकार T<B> contravariant है तो इसे T<D> पर डाला जा सकता है, जैसा कि प्रतीत हो सकता है।

इसके बारे में लगता है कि इस तरह से: अगर कुछ प्रकार T<B> हमेशा एक B की आवश्यकता है, तो यह एक के लिए कदम कर सकते हैं कि हमेशा एक D की आवश्यकता है के बाद से, फिर से, सभी D रों B रों हैं। इनपुट के रूप में (Contents.Add माध्यम से) और आउटपुट के रूप में (Contents संपत्ति खुद के माध्यम से) -

आपका MyContainer वर्ग न covariant है और न ही contravariant क्योंकि इसके प्रकार पैरामीटर दोनों संदर्भों में दिखाई देता है।

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