मैं IEnumerable <T> को कैसे लागू कर सकता हूं


124

मुझे पता है कि इस तरह गैर जेनेरिक IEnumerable कैसे लागू किया जाए:

using System;
using System.Collections;

namespace ConsoleApplication33
{
    class Program
    {
        static void Main(string[] args)
        {
            MyObjects myObjects = new MyObjects();
            myObjects[0] = new MyObject() { Foo = "Hello", Bar = 1 };
            myObjects[1] = new MyObject() { Foo = "World", Bar = 2 };

            foreach (MyObject x in myObjects)
            {
                Console.WriteLine(x.Foo);
                Console.WriteLine(x.Bar);
            }

            Console.ReadLine();
        }
    }

    class MyObject
    {
        public string Foo { get; set; }
        public int Bar { get; set; }
    }

    class MyObjects : IEnumerable
    {
        ArrayList mylist = new ArrayList();

        public MyObject this[int index]
        {
            get { return (MyObject)mylist[index]; }
            set { mylist.Insert(index, value); }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return mylist.GetEnumerator();
        }
    }
}

हालाँकि मैं यह भी देखता हूँ कि IEnumerable का एक सामान्य संस्करण है IEnumerable<T>, लेकिन मैं यह नहीं जान सकता कि इसे कैसे लागू किया जाए।

यदि मैं using System.Collections.Generic;अपने निर्देशों का उपयोग करते हुए जोड़ता हूं, और फिर बदल जाता हूं:

class MyObjects : IEnumerable

सेवा:

class MyObjects : IEnumerable<MyObject>

और फिर राइट क्लिक करें IEnumerable<MyObject>और चुनें Implement Interface => Implement Interface, विजुअल स्टूडियो सहायक कोड के निम्नलिखित ब्लॉक को जोड़ता है:

IEnumerator<MyObject> IEnumerable<MyObject>.GetEnumerator()
{
    throw new NotImplementedException();
}

गैर सामान्य IEnumerable ऑब्जेक्ट को GetEnumerator();विधि से वापस करना इस समय काम नहीं करता है, तो मैं यहां क्या डालूं? सीएलआई अब गैर जेनेरिक कार्यान्वयन की उपेक्षा करता है और जेनेरिक संस्करण के लिए सीधे सिर जाता है जब यह फॉरेस्ट लूप के दौरान मेरे सरणी के माध्यम से गणना करने की कोशिश करता है।

जवाबों:


149

यदि आप एक सामान्य संग्रह का उपयोग करना चुनते हैं, जैसे कि List<MyObject>इसके बजाय ArrayList, आप पाएंगे कि आप List<MyObject>दोनों सामान्य और गैर-सामान्य एन्यूमरेटर प्रदान करेंगे जो आप उपयोग कर सकते हैं।

using System.Collections;

class MyObjects : IEnumerable<MyObject>
{
    List<MyObject> mylist = new List<MyObject>();

    public MyObject this[int index]  
    {  
        get { return mylist[index]; }  
        set { mylist.Insert(index, value); }  
    } 

    public IEnumerator<MyObject> GetEnumerator()
    {
        return mylist.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

1
गैर-सामान्य कार्यान्वयन में, क्या this.GetEnumerator()केवल और केवल लौटने के बीच का अंतर है GetEnumerator()?
टान्नर स्विट

1
@TannerSwett कोई अंतर नहीं है।
मोनरो थॉमस

हो सकता है कि आपको इससे विरासत में मिला हो Collection<MyObject>और फिर आपको कोई कस्टम कोड नहीं लिखना पड़ेगा।
जॉन अलेक्सिउ

@ ja72 क्या होगा यदि आप पहले से ही एक अन्य आधार वर्ग से विरासत में मिले हैं और संग्रह <MyObject> से विरासत में प्राप्त नहीं कर सकते हैं?
मुनरो थॉमस

1
@MonroeThomas - फिर आपको अपने उत्तर में दिखाए अनुसार लपेटना List<T>और कार्यान्वित करना होगा IEnumerable<T>
जॉन एलेक्सी

69

आप शायद (जो आपने दिखाया है) का स्पष्ट कार्यान्वयन नहीं चाहते हैं IEnumerable<T>

उपयोग करने के लिए सामान्य पैटर्न है IEnumerable<T>'s GetEnumeratorकी स्पष्ट कार्यान्वयन में IEnumerable:

class FooCollection : IEnumerable<Foo>, IEnumerable
{
    SomeCollection<Foo> foos;

    // Explicit for IEnumerable because weakly typed collections are Bad
    System.Collections.IEnumerator IEnumerable.GetEnumerator()
    {
        // uses the strongly typed IEnumerable<T> implementation
        return this.GetEnumerator();
    }

    // Normal implementation for IEnumerable<T>
    IEnumerator<Foo> GetEnumerator()
    {
        foreach (Foo foo in this.foos)
        {
            yield return foo;
            //nb: if SomeCollection is not strongly-typed use a cast:
            // yield return (Foo)foo;
            // Or better yet, switch to an internal collection which is
            // strongly-typed. Such as List<T> or T[], your choice.
        }

        // or, as pointed out: return this.foos.GetEnumerator();
    }
}

2
यह एक अच्छा समाधान है यदि SomeCollection <T> पहले से ही IEnumerable <T> को लागू नहीं करता है, या यदि आपको एन्यूमरेशन अनुक्रम में वापस आने से पहले प्रत्येक तत्व पर अन्य प्रसंस्करण को बदलने या करने की आवश्यकता है। यदि इनमें से कोई भी स्थिति सत्य नहीं है, तो यह सिर्फ इसे वापस करने के लिए अधिक कुशल है ।foos.GetEnumerator ();
मोनरो थॉमस

@MonroeThomas: सहमत हैं। कोई सुराग नहीं कि ओपी किस संग्रह का उपयोग कर रहा है।
user7116

8
अच्छी बात। लेकिन कार्यान्वयन IEnumerableनिरर्थक है ( IEnumerable<T>पहले से ही इसे विरासत में मिला है)।
रासना

@ कृष्णा यह सच है, हालाँकि यह भी ध्यान रखें। आईएलस्ट जैसे .NET इंटरफेस को भी अनावश्यक रूप से लागू करते हैं। यह पठनीयता के लिए है - सार्वजनिक इंटरफ़ेस IList: ICollection, IEnumerable
David Klempfner

@Backwards_Dave मैं वास्तव में (अभी भी) लगता है कि यह कम पठनीय बनाता है, क्योंकि यह अनावश्यक शोर जोड़ता है, लेकिन मुझे समझ में आता है कि आपने क्या कहा था, और यह एक मान्य राय है।
रासना

24

आप इसे मैन्युअल रूप से क्यों करते हैं? yield returnपुनरावृत्तियों से निपटने की पूरी प्रक्रिया को स्वचालित करता है। (मैंने अपने ब्लॉग पर इसके बारे में भी लिखा , जिसमें कंपाइलर जनरेट कोड पर एक नज़र भी शामिल है)।

यदि आप वास्तव में इसे स्वयं करना चाहते हैं, तो आपको एक सामान्य एन्यूमरेटर भी वापस करना होगा। ArrayListगैर-जेनेरिक होने के बाद आप किसी भी अधिक का उपयोग नहीं कर पाएंगे । इसे List<MyObject>बदले। यह निश्चित रूप से मानता है कि MyObjectआपके संग्रह में केवल प्रकार की वस्तुएं (या व्युत्पन्न प्रकार) हैं।


2
+1, यह ध्यान दिया जाना चाहिए कि पसंदीदा पैटर्न सामान्य इंटरफ़ेस को लागू करने और गैर-जेनेरिक इंटरफ़ेस को स्पष्ट रूप से लागू करने के लिए है। यील्ड रिटर्न सामान्य इंटरफ़ेस का सबसे प्राकृतिक समाधान है।
user7116

5
जब तक ओपी एन्यूमरेटर में कुछ प्रसंस्करण नहीं कर रहा है, उपज रिटर्न का उपयोग करते हुए बस एक और राज्य मशीन के ओवरहेड को जोड़ता है। ओपी को एक अंतर्निहित जेनेरिक संग्रह द्वारा प्रदान किए गए एन्यूमरेटर को वापस करना चाहिए।
मोनरो थॉमस

@MonroeThomas: आप सही कह रहे हैं। मैंने लिखने से पहले सवाल को बहुत ध्यान से नहीं पढ़ा yield return। मैंने गलत तरीके से माना कि कुछ कस्टम प्रोसेसिंग थे।
एंडर्स हाबिल

4

यदि आप जेनरिक के साथ काम करते हैं, तो ArrayList के बजाय सूची का उपयोग करें। सूची में आपके लिए आवश्यक GetEnumerator विधि है।

List<MyObject> myList = new List<MyObject>();

0

एक में mylist बनाओ List<MyObject>, एक विकल्प है


0

ध्यान दें कि एक अन्य दृष्टिकोण IEnumerable<T>द्वारा पहले से ही लागू की गई System.Collectionsअपनी MyObjectsकक्षा को System.Collectionsआधार वर्ग ( प्रलेखन ) के रूप में प्राप्त करना है :

System.Collections: एक सामान्य संग्रह के लिए आधार वर्ग प्रदान करता है।

हम बाद में हमारे अपने implemenation आभासी ओवरराइड करने के लिए कर सकते हैं System.Collectionsतरीकों कस्टम व्यवहार प्रदान करने के लिए (केवल ClearItems, InsertItem, RemoveItem, और SetItemके साथ Equals, GetHashCode, और ToStringसे Object)। List<T>जिसके विपरीत आसानी से एक्स्टेंसिबल होने के लिए डिज़ाइन नहीं किया गया है।

उदाहरण:

public class FooCollection : System.Collections<Foo>
{
    //...
    protected override void InsertItem(int index, Foo newItem)
    {
        base.InsertItem(index, newItem);     
        Console.Write("An item was successfully inserted to MyCollection!");
    }
}

public static void Main()
{
    FooCollection fooCollection = new FooCollection();
    fooCollection.Add(new Foo()); //OUTPUT: An item was successfully inserted to FooCollection!
}

कृपया ध्यान दें कि collectionकस्टम संग्रह व्यवहार की आवश्यकता होने पर केवल उसी स्थिति में ड्राइविंग की सिफारिश की जाती है, जो शायद ही कभी होता है। उपयोग देखें ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.