2013-08-24 9 views
5

मैं सीखने की कोशिश कर रहा हूं कि सी # के साथ सामान्य वर्ग कैसे बनाएं। क्या कोई इस बात को समझा सकता है कि जब मैं इस प्रोग्राम को चलाता हूं तो मुझे संकलन त्रुटि क्यों मिलती है।सी # जेनेरिक इंटरफेस

मैंने IZooAnimal इंटरफ़ेस बनाया है। सभी चिड़ियाघर जानवर इस इंटरफेस को लागू करेंगे।

public interface IZooAnimal 
{ 
    string Id { get; set; } 
} 

public class Lion : IZooAnimal 
{ 
    string Id { get; set; } 
} 

public class Zebra : IZooAnimal 
{ 
    public string Id { get; set; } 
} 

ZooCage एक ही प्रकार

public class ZooCage<T> where T : IZooAnimal 
{ 
    public IList<T> Animals { get; set; } 
} 

चिड़ियाघर वर्ग के पशुओं पिंजरों

public class Zoo 
{ 
    public IList<ZooCage<IZooAnimal>> ZooCages { get; set; } 
} 

कार्यक्रम कक्षाओं का उपयोग करता है का आयोजन करेगा

class Program 
{ 
    static void Main(string[] args) 
    { 
     var lion = new Lion(); 
     var lionCage = new ZooCage<Lion>(); 
     lionCage.Animals = new List<Lion>(); 
     lionCage.Animals.Add(lion); 

     var zebra = new Zebra(); 
     var zebraCage = new ZooCage<Zebra>(); 
     zebraCage.Animals = new List<Zebra>(); 
     zebraCage.Animals.Add(zebra); 

     var zoo = new Zoo(); 
     zoo.ZooCages = new List<ZooCage<IZooAnimal>>(); 

     zoo.ZooCages.Add(lionCage); 
    } 
} 

जब मैं संकलन मैं follo मिलता है विंग त्रुटि: त्रुटि 2 तर्क 1: 'ConsoleApplication2.ZooCage<ConsoleApplication2.Lion>' से करने के लिए 'ConsoleApplication2.ZooCage<ConsoleApplication2.IZooAnimal>' परिवर्तित नहीं कर सकते

क्या परिवर्तन के क्रम में मेरे कार्यक्रम चलाने बनाने के लिए क्या करना होगा?

+2

कॉन्वर्स और contravariance के बारे में पढ़ें ... शायद [यहां] शुरू करें (http://stackoverflow.com/q/2033912/644812)? –

उत्तर

3

आप अपनी सूची ठोस प्रकार है कि इंटरफ़ेस को लागू करता है के साथ नहीं परिभाषित करना चाहिए, लेकिन इंटरफेस के साथ:

var lionCage = new ZooCage<IZooAnimal>(); 
    lionCage.Animals = new List<IZooAnimal>(); 

फिर अपने कोड अपेक्षित ढंग से काम करेंगे।

प्रारंभिक कोड काम नहीं करता है, क्योंकि इसे ठोस प्रकारों को सामान्यीकृत प्रकार में परिवर्तित करने की अनुमति नहीं है (जैसे @ default.kramer ने covariance and contravariance को इंगित किया है)।

समाधान है कि मैं आया पीछा कर रहा है:

// your ZooCage is still generic 
public class ZooCage<T> 
{ 
    // but you declare on creation which type you want to contain only! 
    private Type cageType = null; 
    public ZooCage(Type iMayContain) 
    { 
     cageType = iMayContain; 
     animals = new List<T>(); 
    } 
    // check on add if the types are compatible 
    public void Add(T animal) 
    { 
     if (animal.GetType() != cageType) 
     { 
      throw new Exception("Sorry - no matching types! I may contain only " + cageType.ToString()); 
     } 
     animals.Add(animal); 
    } 
    // should be generic but not visible to outher world! 
    private IList<T> animals { get; set; } 
} 

इस कोड को आप क्या करने की अनुमति देता है:

var lion = new Lion(); 
    var lionCage = new ZooCage<IZooAnimal>(typeof(Lion)); 
    lionCage.Add(lion); 

    var zebra = new Zebra(); 
    var zebraCage = new ZooCage<IZooAnimal>(typeof(Zebra)); 
    zebraCage.Add(zebra); 

लेकिन यह पर एक त्रुटि फेंक होगा: अब

zebraCage.Add(lion); 

चिड़ियाघर सुरक्षित रूप से बढ़ाया जा सकता है।

+0

+1: हाँ, यह निश्चित रूप से एक हस्ताक्षर मिलान मुद्दा है। – code4life

+2

यह संकलन त्रुटि को रोक देगा, लेकिन यह जेनेरिक का उपयोग करने के पूरे उद्देश्य को हरा देता है। यदि आप ऐसा करने जा रहे हैं तो साथ ही ज़ूकेज जेनेरिक भी बना सकते हैं। यह आवश्यकता को पूरा नहीं करता है "चिड़ियाघर एक ही प्रकार के जानवरों को पकड़ लेगा", क्योंकि सभी पिंजरे किसी भी प्रकार के जानवरों को अनुमति देंगे। – JLRishe

+0

समस्या यह है कि ओपी चाहता है कि यह संभव नहीं है, न केवल इसलिए कि संकलक ऐसा कहता है, लेकिन क्योंकि संकलन-समय प्रकार सुरक्षा सत्यापन खिड़की से बाहर निकलता है। संकलक यह सत्यापित करने में सक्षम नहीं होगा कि अगर आप उस वाक्यविन्यास की अनुमति देते हैं तो कोड सुरक्षित है, इसलिए आप सबकुछ के लिए रनटाइम चेक पर वापस आ जाएंगे। तो इससे कोई फर्क नहीं पड़ता कि आप इसे कैसे पासा करते हैं, यह संभव नहीं है क्योंकि यह एक अच्छा विचार नहीं है। –

2

चूंकि आप कई पिंजरे रखना चाहते हैं, लेकिन प्रत्येक प्रकार के पिंजरे में केवल एक जानवर हो सकता है, आपका मॉडल थोड़ा दूर है।

  • IZooAnimal अपरिवर्तित है:

    मैं इस प्रकार कोड दुबारा लिखा।

  • एक कॉन्वर्स इंटरफेस ICage है जो IZooAnimal किसी भी प्रकार को स्वीकार करता है। यह आपको हर प्रकार के जानवरों के लिए दृढ़ता से टाइप किए गए पिंजरे की अनुमति देता है।
  • फिर, मेरे पास CageICage का ठोस कार्यान्वयन है। Cage सामान्य है, लेकिन आप इसे आसानी से एक अमूर्त वर्ग बना सकते हैं और फिर पशु-विशिष्ट पिंजरे कार्यान्वयन कर सकते हैं। उदाहरण के लिए, यदि आपके ज़ेबरा को घास खिलाया जाना चाहिए, और आपके शेर को मांस खिलाया जाना चाहिए, तो आप अपने पिंजरों के कार्यान्वयन को विशेषज्ञ बना सकते हैं।

यहाँ पूरा कोड है:

public interface IZooAnimal 
{ 
    string Id { get; set; } 
} 

public interface ICage<out T> where T : IZooAnimal 
{ 
    IReadOnlyCollection<T> Animals { get; } 
} 

public class Cage<T> : ICage<T> where T: IZooAnimal 
{ 
    private readonly List<T> animals = new List<T>(); 

    public IReadOnlyCollection<T> Animals 
    { 
     get 
     { 
      return animals.AsReadOnly(); 
     } 
    } 

    public void CageAnimal(T animal) 
    { 
     animals.Add(animal); 
    } 
} 

public class Lion : IZooAnimal 
{ 
    public string Id { get; set; } 
} 

public class Zebra : IZooAnimal 
{ 
    public string Id { get; set; } 
} 

public class Zoo 
{ 
    public IList<ICage<IZooAnimal>> Cages { get; set; } 
} 

internal class Program 
{ 

    private static void Main(string[] args) 
    { 
     var lion = new Lion(); 
     var zebra = new Zebra(); 
     var lionCage = new Cage<Lion>(); 
     lionCage.CageAnimal(lion); 

     var zebraCage = new Cage<Zebra>(); 
     zebraCage.CageAnimal(zebra); 

     var zoo = new Zoo(); 
     zoo.Cages.Add(lionCage); 
     zoo.Cages.Add(zebraCage); 

    } 
} 
3

@ DanielMann का जवाब काफी अच्छा है, लेकिन एक खामी से ग्रस्त है: मूल IList इंटरफ़ेस ICage इंटरफेस के साथ नहीं किया जा सकता। इसके बजाए, आईसीएजी को एक ReadOnlyCollection का पर्दाफाश करना है, और CageAnimal नामक एक नई विधि का पर्दाफाश करना है।

मैंने एक समान दृष्टिकोण का उपयोग करके कोड को फिर से लिखा है। मेरा ICage कार्यान्वयन बहुत कमजोर है, लेकिन यह आपको अंदर IList अर्थशास्त्र के साथ चिपकने की अनुमति देता है।

public interface IZooAnimal 
{ 
    string Id { get; set; } 
} 

public class Lion : IZooAnimal 
{ 
    public string Id { get; set; } 
} 

public class Zebra : IZooAnimal 
{ 
    public string Id { get; set; } 
} 

public interface ICage 
{ 
    IEnumerable<IZooAnimal> WeaklyTypedAnimals { get; } 
} 

public class Cage<T> : ICage where T : IZooAnimal 
{ 
    public IList<T> Animals { get; set; } 

    public IEnumerable<IZooAnimal> WeaklyTypedAnimals 
    { 
     get { return (IEnumerable<IZooAnimal>) Animals; } 
    } 
} 

public class Zoo 
{ 
    public IList<ICage> ZooCages { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var lion = new Lion(); 
     var lionCage = new Cage<Lion>(); 
     lionCage.Animals = new List<Lion>(); 
     lionCage.Animals.Add(lion); 

     var zebra = new Zebra(); 
     var zebraCage = new Cage<Zebra>(); 
     zebraCage.Animals = new List<Zebra>(); 
     zebraCage.Animals.Add(zebra); 

     var zoo = new Zoo(); 
     zoo.ZooCages = new List<ICage>(); 

     zoo.ZooCages.Add(lionCage); 
    } 
} 
+0

उत्कृष्ट बिंदु। मैंने आपका दृष्टिकोण माना! :) –

+0

शेर त्वचा के लिए बहुत सारे तरीके हैं। :) – CSJ

+0

वास्तव में - मेरे से अधिक सुरुचिपूर्ण और क्लीनर समाधान! :) – pasty

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