c# सी#IEnumerable, IEnumerator रीसेट समारोह कॉल नहीं मिलता है




foreach (4)

मैं मूल रूप से मेरी कक्षा को विदेशी मुद्रा का प्रयोग करने में सक्षम बनाने की कोशिश कर रहा हूं मैंने इस ट्यूटोरियल को पढ़ा। एमएसडीएन यह बहुत सीधे आगे लगता है हालांकि, जब मुझे दूसरी बार पुनरावृति करना है तो मुझे एक समस्या है मैंने इसे डीबग किया; और यह पता चला कि यह Reset() फ़ंक्शन कॉल नहीं करता है

कक्षा

class A : IEnumerable, IEnumerator
{
    int[] data = { 0, 1, 2, 3, 4 };

    int position = -1;

    public object Current
    {
        get
        {
            return data[position];
        }
    }

    public bool MoveNext()
    {
        position++;
        return (position < data.Length);
    }

    public void Reset()
    {
        position = -1;
    }

    public IEnumerator GetEnumerator()
    {
        return (IEnumerator)this;
    }
}

जब मैं निम्नलिखित मुख्य फ़ंक्शन चलाता हूं; यह Reset() फ़ंक्शन कभी कॉल नहीं करता है। इसलिए, एक पाश के बाद मैं कभी भी फिर से मेरी कक्षा को फिर से सक्षम नहीं कर सकता।

मुख्य

static void Main(string[] args)
{
    A a = new A();

    foreach (var item in a)
    {
        Console.WriteLine(item);
    }

    Console.WriteLine("--- First foreach finished. ---");

    foreach (var item in a)
    {
        Console.WriteLine(item);
    }
}

आउटपुट:

0
1
2
3
4
--- First foreach finished. ---
Press any key to continue . . .

कोई विचार?


रीसेट () मूल रूप से एक गलती थी यदि संभव हो तो एक साफ गणक प्राप्त करने के लिए पहले से ही एक ज्ञात विधि है: GetEnumerator ()

यह विनिर्देश में एक आवश्यकता है कि इटरेटर ब्लॉक कार्यान्वयन (इटरेटर के लिए) इस पद्धति के लिए एक अपवाद फेंक देता है, इसलिए सामान्य तौर पर यह औपचारिक रूप से ज्ञात है कि यह काम करने की उम्मीद नहीं की जा सकती है, और बस: कोई भी कभी इसे कॉल नहीं करता है सच कहूँ तो, उन्हें एपीआई पर [अप्रचलित] भी चिह्नित करना चाहिए!

इसके अतिरिक्त, कई अनुक्रम गैर-पुनरावर्ती हैं एक नेटवर्कस्ट्रीम या एक यादृच्छिक संख्या जनरेटर पर बैठे Iterators के बारे में सोचो। चूंकि इटरेटर्स (सामान्य मामले में) को दोहराने योग्य नहीं होने की आवश्यकता है, इसलिए आपको लक्ष्य करना चाहिए, जहां संभव हो, उन्हें सबसे अधिक बार पुनरावृति करना चाहिए। शायद ToList () के माध्यम से बफरिंग अगर यह संभव नहीं है

"foreach" किसी भी बिंदु पर रीसेट () को शामिल नहीं करता है बस GetEnumerator (), MoveNext (), वर्तमान और डिस्पोज़ ()।


दोनों रीड और कार्लोस सही हैं। यहां एक तरीका है, int[] आप ऐसा कर सकते हैं, चूंकि IEnumerable और IEnumerable<int>

class A : IEnumerable
{ 
    int[] data = { 0, 1, 2, 3, 4 }; 

    public IEnumerator GetEnumerator() 
    { 
        return data.GetEnumerator(); 
    } 
} 

या, अधिक दृढ़ता से टाइप करने के लिए, आप IEnumerable के सामान्य रूप का उपयोग कर सकते हैं:

class A : IEnumerable<int>
{
    int[] data = { 0, 1, 2, 3, 4 };

    public IEnumerator<int> GetEnumerator()
    {
        return ((IEnumerable<int>)data).GetEnumerator();
    }

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

प्रत्येक बार foreach कहा जाता है, यह एक नया IEnumerator लिए पूछता है अपना क्लास इम्प्रेशन लौटाना एक बुरा विचार है - आपको IEnumerator को लागू करने के लिए एक अलग क्लास बनाना चाहिए, और इसके बदले उसे वापस करना चाहिए।

यह आमतौर पर एक नेस्टेड (निजी) कक्षा का उपयोग करके किया जाता है, और इसका एक उदाहरण लौटाता है आप निजी वर्ग के लिए क्लास ए इंस्टेंस को पास कर सकते हैं (इसे data तक पहुंच सकते हैं), और उस कक्षा में position फ़ील्ड डाल सकते हैं। यह एक से अधिक गणक को समानांतर बनाने के लिए अनुमति देगा, और बाद के अग्रगमन कॉलों के साथ ठीक से काम करेगा।

उदाहरण के लिए, अपना कोड संशोधित करने के लिए, आप ऐसा कुछ करना चाहते हैं:

using System;
using System.Collections;

class A : IEnumerable
{
    int[] data = { 0, 1, 2, 3, 4 };

    public IEnumerator GetEnumerator()
    {
        return new AEnumerator(this);
    }

    private class AEnumerator : IEnumerator
    {
        public AEnumerator(A inst)
        {
            this.instance = inst;
        }

        private A instance;
        private int position = -1;

        public object Current
        {
            get
            {
                return instance.data[position];
            }
        }

        public bool MoveNext()
        {
            position++;
            return (position < instance.data.Length);
        }

        public void Reset()
        {
            position = -1;
        }

    }
}

ध्यान दें कि आप सरणी के गणक को सीधे वापस भी लौटा सकते हैं (हालांकि मैं यह सोच रहा था कि आप साफ गणना करने के तरीके सीखना चाहते थे):

class A : IEnumerable
{
    int[] data = { 0, 1, 2, 3, 4 };

    public IEnumerator GetEnumerator()
    {
        return data.GetEnumerator();
    }
}

अंत में, आप इटरेटर का उपयोग इसे एक सरल तरीके से कार्यान्वित करने के लिए कर सकते हैं:

class A : IEnumerable
{
    int[] data = { 0, 1, 2, 3, 4 };

    public IEnumerator GetEnumerator()
    {
        for (int i=0;i<data.Length;++i)
           yield return data[i];
    }
}

कहा जा रहा है, मैं IEnumerable<int> अलावा IEnumerable<int> को लागू करने की जोरदार अनुशंसा करेगा जेनेरिक उपयोग के मामले में यह बहुत अच्छा बनाते हैं।


गणना Reset कॉल नहीं करता है आपको एक गणक के एक नए उदाहरण को बनाने की आवश्यकता है, जिसका मतलब होगा कि गणक के लिए एक अलग वर्ग बनाना (यानी, IEnumerable और IEnumerator के लिए एक ही प्रकार का उपयोग न करें), नीचे दिए गए कोड की तरह:

public class _11475328
{
    class A : IEnumerable
    {
        int[] data = { 0, 1, 2, 3, 4 };

        public IEnumerator GetEnumerator()
        {
            return new AEnumerator(this);
        }

        class AEnumerator : IEnumerator
        {
            private A parent;
            private int position = -1;

            public AEnumerator(A parent)
            {
                this.parent = parent;
            }

            public object Current
            {
                get { return parent.data[position]; }
            }

            public bool MoveNext()
            {
                position++;
                return (position < parent.data.Length);
            }

            public void Reset()
            {
                position = -1;
            }
        }
    }
    public static void Test()
    {
        A a = new A();

        foreach (var item in a)
        {
            Console.WriteLine(item);
        }

        Console.WriteLine("--- First foreach finished. ---");

        foreach (var item in a)
        {
            Console.WriteLine(item);
        }
    }
}




enumerator