none
IEnumerator for Dictionary<key, value> RRS feed

  • Question

  • I am trying to find a simple, clean example of GetEnumerator() for a Dictionary<key, value> class. All I can find are examples for lists and arrays.

    I have written a class that looks like an enumerator class, but if the Current object is of type Object, I get an error that it is not of type 'MyValue'. Change the current type to type 'MyValue' and I get the inverse error: Current is not of type Object. Can't have two Current properties, so how to get around this?

    IEnumerator class declaration:

    MyValueEnumerator : IEnumerator<MyValue>

    The MyValue class does have the System.Collections.IEnumerator ... call.

    VS 2010, C#, .Net 4

    Clearly, I'm missing something, but can't see what.

    Thanks,

    RK

    Tuesday, May 5, 2015 7:51 PM

Answers

  • Oops! My bad.

    This works: foreach (KeyValuePair <int, Person> kvp in bigP.people) { ... use kvp.Key and kvp.Value ...}

    Thanks again for the help.

    RK

    • Marked as answer by roadkill4299 Wednesday, May 6, 2015 4:02 PM
    Wednesday, May 6, 2015 4:02 PM

All replies

  • If I understand correctly you have a problem implementing Current because there's IEnumerator.Current and IEnumerator<T>.Current and both need to be implemented as IEnumerator<T> inherits IEnumerator.

    This is solved by using explicit interface implementation:

    class En : IEnumerator<MyValue>
    {
        public MyValue Current
        {
            get { return ...; }
        }
    
        object IEnumerator.Current // implement non-generic IEnumerator.Current
        {
            get { return Current; }
        }
        ...

    This is not specific to dictionaries, all generic enumerators have to do this.

    What's specific to dictionaries is that their enumerator normally enumerates KeyValuePair<K,V> objects, not just key or value objects.

    Tuesday, May 5, 2015 8:23 PM
    Moderator
  • Mike:

    Thanks for the insight. One of the problems was that I was inadvertently using the generic IEnumerator which requires a parameter; I added "using System.Collections;" to handle the problem with Current. Many changes later, the best I can do is get an error message: "Cannot convert type ConsoleApplication8.Person' to 'System.Collections.Generic.KeyValuePair<int, ConsoleAppllication8.Person>'. The line number is the 'foreach' call:

    People bigP = new People();

    foreach (KeyValuePair<int, Person> kvp in bigP)

    { string s = kvp.Value.name; }

    Here are the class definitions:

        public class Person
        {
            public string name;
            public int age;
            public List<string> folks = new List<string>();
            public Dictionary<int, string> dict = new Dictionary<int, string>();
            public Person()
         }
    
        public class People : IEnumerable<Person>
        {
            public Dictionary<int, Person> people = new Dictionary<int, Person>();
    
            public IEnumerator<Person> GetEnumerator()       
            {
                PeopleEnumerator pe = new PeopleEnumerator();
                return pe;
            }
    
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {            
                return this.GetEnumerator();
            }
    
        }
    
        public class PeopleEnumerator : IEnumerator<Person>
        {
            Person per = new Person();
            public bool MoveNext()
            {
                return false;
            }
            public Person Current
            {
                get { return per; }
            }
           
            object IEnumerator.Current
            {
                get { return Current; }
            }
    
            public void Reset()
            {
            }
            public void Dispose()
            {
            }
        }

    Any further suggestions will very much appreciated.

    RK

    Wednesday, May 6, 2015 1:27 PM
  • "foreach (KeyValuePair<int, Person> kvp in bigP)"

    Sorry, my remark about KeyValuePair was probably confusing. When you enumerate Dictionary<K,V> you get KeyValuePair<K,V> objects. But that's not what you want to do it seems, you want to enumerate just the values so KeyValuePair doesn't belong here, it's simply "foreach (Person p in bigP)".

    I'm not sure what you're trying to do with that enumerator. You can simply use the one provided by dictionary's value collection:

    public class People : IEnumerable<Person>
    {
        public Dictionary<int, Person> people = new Dictionary<int, Person>();
    
        public IEnumerator<Person> GetEnumerator()       
        {
            return people.Values.GetEnumerator();
        }
    
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {            
                return this.GetEnumerator();
        }
    }
    

    Wednesday, May 6, 2015 2:44 PM
    Moderator
  • Mike:

      Thanks again for the help. Sorry but the example was only intended to see that the 'foreach' loop was working properly; this is a simple learning tool that will be used in larger projects. 

    Tried your idea, it doesn't compile, same error message: "Cannot convert type 'ConsoleApplication.Person' to 'System.Collections.Generic.KeyValuePair<int, ConsoleApplication8.Person>'". I just don't understand the error message. What is it trying to tell me?

       I've tried writing something that looks like a real iterator, that is, implemented the various methods, so if I can get around the compiler's objection, maybe it will work - maybe.

    The updated Iterator class:

    public class PeopleEnumerator : IEnumerator<Person>    
        {
            public PeopleEnumerator(People people)
            {
                ppl = people;
            }
            Person per = new Person();
            People ppl = new People();
    
            public int position = -1;
    
            public bool MoveNext()
            {
                //return false;
                position++;
                return (position < ppl.CountP);
            }
    
            public Person Current
            {
                //get { return per; }
                get
                {
                    if (position < ppl.CountP)
                    {
                        per = ppl.people[position];
                        return ppl.people[position];
                    }
                    else
                        return null;
                }
            
            }
           
            object IEnumerator.Current        
            {
                get { return per; }
            }
    
            public void Reset()
            {
                position = -1;
            }
            public void Dispose()
            {
            }
        }

    This does compile, except for the error on the 'foreach' call.

    Thanks again,

    RK

    Wednesday, May 6, 2015 3:21 PM
  • "Tried your idea, it doesn't compile, same error message: "Cannot convert type 'ConsoleApplication.Person' to 'System.Collections.Generic.KeyValuePair<int, ConsoleApplication8.Person>'". I just don't understand the error message. What is it trying to tell me?"

    It is telling you that the foreach variable has one type and the enumerator uses another type. Like in trying to enumerate integers from a string array:

    foreach (int x in new[] { "foo", "bar" }) ...

    Your foreach needs to be

    foreach (Person p in bigP)

    it seems that you're still using the KeyValuePair version.

    Wednesday, May 6, 2015 3:30 PM
    Moderator
  • Oops! My bad.

    This works: foreach (KeyValuePair <int, Person> kvp in bigP.people) { ... use kvp.Key and kvp.Value ...}

    Thanks again for the help.

    RK

    • Marked as answer by roadkill4299 Wednesday, May 6, 2015 4:02 PM
    Wednesday, May 6, 2015 4:02 PM
  • That works but it doesn't use your enumerator :)
    Wednesday, May 6, 2015 4:14 PM
    Moderator