2010-08-20 20 views
16

पर नहीं डाला जा सकता है मुझे पता है कि यह पुराना है, फिर भी मैं उन समस्याओं को समझने के लिए अभी भी बहुत अच्छा नहीं हूं। क्या कोई मुझे बता सकता है कि निम्नलिखित क्यों काम नहीं करता है (कास्टिंग के बारे में runtime अपवाद फेंकता है)?जेनिक्स और कास्टिंग - विरासत वर्ग को बेस क्लास

public abstract class EntityBase { } 
public class MyEntity : EntityBase { } 

public abstract class RepositoryBase<T> where T : EntityBase { } 
public class MyEntityRepository : RepositoryBase<MyEntity> { } 

और अब कास्टिंग लाइन:

MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever 
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo; 

तो, किसी को समझा सकते हैं कि यह कैसे अमान्य है? और, मैं समझाने के मूड में नहीं हूं - क्या इस कोड को वास्तव में करने के लिए मैं कोड का एक लाइन उपयोग कर सकता हूं?

+1

उत्तर के लिए सभी को धन्यवाद। इसे कम करने के लिए - अब मैंने बेस इंटरफ़ेस के साथ इस समस्या का समाधान किया (रिपोजिटरीबेस : आईरिपोजिटरी)। बाहर निकलता है मुझे बस उस उदाहरण पर कार्यों को निष्पादित करने की आवश्यकता होती है जो मुझे मिलती है और कक्षा को अन्य चीजों को संभालने देती है। – Jefim

+0

[सी # कोविरिएन्स और कंट्रावायरेंस एफएक्यू] देखें (http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx) –

उत्तर

24

RepositoryBase<EntityBase>MyEntityRepository का आधार वर्ग नहीं है। आप जेनेरिक भिन्नता की तलाश कर रहे हैं जो सीमित सीमा तक सी # में मौजूद है, लेकिन यहां लागू नहीं होगा।

मान लीजिए कि आपकी RepositoryBase<T> वर्ग इस तरह की एक विधि था:

void Add(T entity) { ... } 

अब विचार करें:

MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever 
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo; 
baseRepo.Add(new OtherEntity(...)); 

अब आप एक MyEntityRepository को संस्था की एक अलग तरह की जोड़ दिया है ... और जो कर सकते हैं सही नहीं है।

असल में, सामान्य भिन्नता केवल कुछ स्थितियों में सुरक्षित है। विशेष रूप से सामान्य covariance (जो आप यहां वर्णन कर रहे हैं) केवल तभी सुरक्षित है जब आप केवल API के मान "आउट" प्राप्त करते हैं; जेनेरिक contravariance (जो दूसरे तरीके से काम करता है) केवल तब सुरक्षित होता है जब आप कभी भी एपीआई में "मूल्य" डालते हैं (उदाहरण के लिए एक सामान्य तुलना जो क्षेत्र के किसी भी दो आकार की तुलना कर सकती है उसे वर्गों की तुलना के रूप में माना जा सकता है)।

सी # 4 में यह जेनेरिक इंटरफेस और जेनेरिक प्रतिनिधियों के लिए उपलब्ध है, कक्षाओं में नहीं - और केवल संदर्भ प्रकारों के साथ। अधिक जानकारी के लिए MSDN देखें, < प्लग > पढ़ें C# in Depth, 2nd edition, अध्याय 13 </प्लग > या एरिक लिपर्ट blog series विषय पर पढ़ें। इसके अलावा, मैंने जुलाई 2010 में एनडीसी में इस बारे में एक घंटे की बात की - वीडियो उपलब्ध है here; बस "भिन्नता" के लिए खोजें।

+0

धन्यवाद जॉन, दोनों के लिए स्पष्टीकरण और बहुत रोचक सामग्री के लिए लिंक। मैं निश्चित रूप से पुस्तक पढ़ने और वीडियो देखने पर विचार करूंगा। – Jefim

6

इसके लिए कॉन्वर्सिस या contravariance की आवश्यकता है, जिसका समर्थन नेट में सीमित है, और सार वर्गों पर उपयोग नहीं किया जा सकता है। आप हालांकि इंटरफेस पर भिन्नता का उपयोग कर सकते हैं, इसलिए आपकी समस्या का एक संभावित समाधान एक सारणी बनाने के लिए है जिसे आप अमूर्त वर्ग के स्थान पर उपयोग करते हैं। लेकिन मैं इसे एक कदम और आगे ले रहा हूँ द्वारा;

public interface IRepository<out T> where T : EntityBase { //or "in" depending on the items. 
    } 
    public abstract class RepositoryBase<T> : IRepository<T> where T : EntityBase { 
    } 
    public class MyEntityRepository : RepositoryBase<MyEntity> { 
    } 

    ... 

    IRepository<EntityBase> baseRepo = (IRepository<EntityBase>)myEntityRepo; 
11

जब भी किसी को इस सवाल पूछता है, मैं उनके उदाहरण लेते हैं और अधिक अच्छी तरह से ज्ञात कक्षाओं कि स्पष्ट रूप से गैर कानूनी है का उपयोग कर कुछ करने के लिए यह अनुवाद करने का प्रयास (यह क्या Jon Skeet has done in his answer है इस अनुवाद का प्रदर्शन)।

के MyStringList साथ MyEntityRepository की जगह, इस तरह करते हैं:

class MyStringList : List<string> { } 

अब, आप MyEntityRepositoryRepositoryBase<EntityBase> को castable होना चाहता हूँ लगता है, तर्क किया जा रहा है इस के बाद से MyEntityEntityBase से निकला संभव होना चाहिए कि।

लेकिन stringobject से निकला है, है ना? तो इस तर्क से हमें MyStringList को List<object> पर डालने में सक्षम होना चाहिए।

चलो देखते हैं क्या हो सकता है अगर हम इसकी अनुमति ...

var strings = new MyStringList(); 
strings.Add("Hello"); 
strings.Add("Goodbye"); 

var objects = (List<object>)strings; 
objects.Add(new Random()); 

foreach (string s in strings) 
{ 
    Console.WriteLine("Length of string: {0}", s.Length); 
} 

ओह। अचानक हम List<string> पर गिन रहे हैं और हम Random ऑब्जेक्ट पर आते हैं। यह अच्छा नहीं है।

उम्मीद है कि यह समस्या को समझने में थोड़ा आसान बनाता है।

+4

मैंने उदाहरणों में एक कदम आगे बढ़ना शुरू कर दिया है - मैं स्ट्रिंग और ऑब्जेक्ट का उपयोग करता था, लेकिन एरिक लिपर्ट के सुझाव पर मैंने वास्तविक दुनिया वस्तुओं का उपयोग शुरू कर दिया है ... फल/ऐप्पल/केले "आप कर सकते हैं" के संदर्भ में अच्छी तरह से काम करता है केले के गुच्छा में एक सेब जोड़ें "। –

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