2012-02-02 31 views
5

मैं सी # 4.0 जेनरिक के साथ व्यस्त हो गया है, और अब मैं मूल रूप से इस तरह कुछ करना चाहता हूँ:सी # - क्या सामान्य संग्रह डालने का कोई तरीका है?

public abstract class GenericTree<T> : Tree 
    where T : Fruit 
{ 
    public Tree(IFruitCollection<T> fruits) 
     : base(fruits) { } 
} 

आधार ट्री वर्ग इस तरह दिखता है:

public abstract class Tree 
{ 
    private IFruitCollection<Fruit> fruits; 

    public IFruitCollection<Fruit> GetFruits 
    { 
     get { return fruits; } 
    } 

    public Tree(IFruitCollection<Fruit> fruits) 
    { 
     this.fruits = fruits; 
    } 
} 

यहाँ मेरी पहली समस्या है । जेनेरिक ट्री का कन्स्ट्रक्टर जेनेरिक संग्रह को फलों के संग्रह में नहीं डाल सकता है। मुझे जेनेरिक ट्री का कार्यान्वयन भी मिला है:

public class AppleTree : GenericTree<Apple> 
{ 
    public AppleTree() 
     : base(new FruitCollection<Apple>) { } 
} 

मेरी दूसरी समस्या यहां दी गई है। जब मैं AppleAree के उदाहरण में फल जोड़ता हूं, myAppleTree.GetFruits का उपयोग करके। जोड़ें (...), मैं केवल सेब तक ही सीमित नहीं हूं। मुझे सभी प्रकार के फल जोड़ने की अनुमति है। मुझे यह नहीं चाहिए।

मैं GenericTree को यह जोड़कर इस समस्या का समाधान करने की कोशिश की:

new public IFruitCollection<T> GetFruits 
{ 
    get { return base.GetFruits as IFruitCollection<T>; } 
} 

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

IFruitCollection इंटरफ़ेस इस तरह दिखता है:

public interface IFruitCollection<T> : ICollection<T> 
    where T : Fruit { ... } 

और FruitCollection वर्ग संग्रह वर्ग के एक सरल कार्यान्वयन है। ओह, और निश्चित रूप से, ऐप्पल वर्ग फल वर्ग को बढ़ाता है।

समाधान IFruitCollection इंटरफेस को कॉन्वर्सिस और contravariance दोनों के साथ संगत बनाने के लिए है। लेकिन मैं इसे कैसे प्राप्त करूं? "इन" या "आउट" पैरामीटर कीवर्ड संभव नहीं हैं, क्योंकि आईसीओलेक्शन इंटरफेस इसे अनुमति नहीं देता है।

आपकी मदद के लिए बहुत बहुत धन्यवाद!

+0

कृपया ध्यान दें कि यह आसानी से एक में ट्री और GenericTree वर्ग का विलय करके हल किया जा सकता। लेकिन याद रखें कि यह मेरी समस्या है कि यह केवल एक उदाहरण है। मेरा अपना आवेदन थोड़ा और जटिल है। –

+0

जैसा कि आप इंगित करते हैं, समाधान ट्री जेनेरिक बनाना है। आप यह भी मानते हैं कि यह संभव नहीं है। क्यों नहीं? – phoog

+0

किसी बिंदु पर, मुझे पेड़ की एक सूची की आवश्यकता है। और वृक्ष की एक सूची यह नहीं करती है, क्योंकि मुझे पेड़ उदाहरण जोड़ने की अनुमति नहीं है। यह निश्चित रूप से कहता है कि वृक्ष और वृक्ष के बीच कोई अंतर्निहित संदर्भ नहीं है। –

उत्तर

1

मुझे लगता है कि मुझे अपना समाधान मिला, जबकि मैं फूग के कामकाज की कोशिश कर रहा था। धन्यवाद, फूग!

पहले मैंने जेनेरिकट्री को अपना खुद का फलकोलेक्शन नहीं दिया था। मोस्टी क्योंकि बेस क्लास अक्सर अपने संग्रह के माध्यम से लूप्स, उदाहरण के लिए फल अपडेट करने के लिए। इसके परिणामस्वरूप फलों को अद्यतन नहीं किया गया, क्योंकि उन्हें जेनेरिक ट्री के संग्रह में जोड़ा गया था, न कि मूल संग्रह के लिए।

लेकिन आखिरकार मुझे एहसास हुआ कि उन संग्रहों को कास्टिंग कभी भी काम नहीं करेगा।

अब मैंने एक और फलकोलेक्शन क्लास बनाया है जो स्वचालित रूप से आधार को फलोंकोलेक्शन के आधार पर जोड़ता है और हटा देता है, और इसके विपरीत। यह समाधान मेरे लिए बहुत अच्छा काम करता है!

public FruitCollection<T, U> : FruitCollection<T> 
    where T : U 
    where U : Fruit 
{ 
    private FruitCollection<U> baseCollection; 

    public FruitCollection(FruitCollection<U> baseCollection) 
     : base() 
    { 
     this.baseCollection = baseCollection; 

     // here I added code that throws events whenever the collection is changed 
     // I used those events to add/remove fruit to the base collection, and vice versa 
     // see the link below. 
     ... 
    } 

    ... 
} 

public class GenericTree<T>: Tree 
    where T : Fruit 
{ 
    private FruitCollection<T> fruits; 

    // use the new keyword to hide the base collection 
    new public FruiCollection<T> Fruits 
    { 
     get { return fruits; } 
    } 

    public GenericTree() 
     : base() 
    { 
     // after hiding the base collection, use base.Fruits to get it 
     fruits = new FruitCollection<T, Fruit>(base.Fruits); 
    } 
} 

यह मैं मदद की: How to handle add to list event?

2

अपनी टिप्पणी के जवाब में:

कुछ बिंदु पर, मैं पेड़ की एक सूची की जरूरत है। और Tree<Fruit> की एक सूची ऐसा नहीं करती है, क्योंकि मुझे Tree<Banana> उदाहरण जोड़ने की अनुमति नहीं है। यह निश्चित रूप से कहता है कि Tree<Fruit> और Tree<Banana> के बीच कोई अंतर्निहित संदर्भ नहीं है।

मूल समस्या यह है कि आप संग्रह करना चाहते हैं - पेड़ों की सूची - उस (परोक्ष रूप से) में विभिन्न प्रकार की समान वस्तुएं होती हैं। Tree (जो कि Fruit का संग्रह भी है) से संग्रह को असंबद्ध करने के लिए, चलो इसे Orchard पर कॉल करें।

ऑर्चर्ड - (शामिल है) -> पेड़ - आप एक गैर सामान्य Tree, Orchard के तत्वों करते हैं कि प्रकार के सभी हो सकता है> फल

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

वैकल्पिक रूप से, आप एक सामान्य Tree<T> where T : Fruit वर्ग बना सकता है, तो आप पेड़ में निहित Fruit के उपवर्ग के संबंध में प्रकार सुरक्षा की है। इसका मतलब है कि Orchard में विभिन्न प्रकार की वस्तुओं को शामिल किया जाएगा, फिर से रनटाइम प्रकार की जांच की आवश्यकता होगी।

(आप प्रत्येक प्रकार के लिए एक अलग एक्सेसर की घोषणा के द्वारा स्थिर प्रकार सुरक्षा के साथ एक ऑर्चर्ड कर सकता है:

class Tree { } 
class Tree<T> : Tree { } 
class Trees : IEnumerable<Tree> 
{ 
    Tree<Banana> _bananaTree; 
    Tree<Apple> _appleTree; 
    //...etc. 

    Tree<Banana> GetBananaTree() { return _bananaTree; } 
    Tree<Apple> GetBananaTree() { return _appleTree; } 
    //...etc. 

    public IEnumerator<Tree> GetEnumerator() 
    { 
     yield return _bananaTree; 
     yield return _appleTree; 
     //...etc. 
    } 
} 

लेकिन वह शायद नहीं है तुम क्या चाहते है, तो आप एक डाली कहीं की आवश्यकता है।)

मुझे लगता है कि यदि आप इसके बजाय Tree की तुलना में Orchard में डाली, जिस स्थिति में मैं अंतर्निहित दृष्टिकोण के लिए कुछ इस तरह का सुझाव होगा:

IDictionary<Type, object> orchard = new Dictionary<Type, object>(); 

//to retrieve the tree 
Tree<Banana> bananaTree = (Tree<Banana>)orchard[typeof(Banana)]; 

(बेशक, आप एक अधिक विशिष्ट प्रकार object से Tree, या ICollection, या IEnumerable की तरह इस्तेमाल कर सकते हैं,।)

मैं आगे जाकर एक Orchard कक्षा में उस तर्क को संपुटित, जो प्रदान कर सकता हैं एक कम एक्सेसर में डाली प्रदर्शन से वर्बोज़ वाक्य रचना:

var bananaTree = orchard.GetTree<Banana>(); 

जहां:

public class Orchard 
{ 
    private IDictionary<Type, object> _trees; 

    //... 

    public Tree<T> GetTree<T>() 
    { 
     return (Tree<T>)_trees[typeof(T)]; 
    } 
} 

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

+0

आपके उत्तर के लिए धन्यवाद।कल मुझे एहसास हुआ कि मुझे गैर-जेनेरिक वृक्ष वर्ग से छुटकारा पाने की जरूरत है, और आपने व्यावहारिक रूप से पुष्टि की है कि मेरे लिए। आपका ऑर्चर्ड क्लास एक महान कामकाज की तरह लगता है। मुझे डर है कि यह मेरे 'वास्तविक' आवेदन के लिए लागू नहीं होगा, मैं कल इसे आजमाउंगा। –

+0

आपका कामकाज मेरे लिए काम करता था, लेकिन मुझे तुम्हारा उपयोग करते समय एक और कामकाज मिला। मेरे अपने जवाब पर एक नज़र डालें। यद्यपि कि आपकी इस सहायता के लिए धन्यवाद! –

+0

@ रुडी यह एक दिलचस्प समाधान है, और यदि आपके पास बहुत सारी वस्तुएं नहीं हैं तो यह ठीक काम करेगी। मेरे पास एक प्रोजेक्ट है जहां संग्रह में लाखों ऑब्जेक्ट्स हैं, और अलग-अलग टाइप किए गए संदर्भों के लिए * दो * संग्रह रखने के संग्रहण ओवरहेड को अस्वीकार्य माना जाएगा। – phoog

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