B
B
BadCats2016-09-10 19:55:06
C++ / C#
BadCats, 2016-09-10 19:55:06

foreach loop when dealing with collections and arrays?

Hello. I started looking for collections and the foreach loop used to work with them.
There is a banal, so to speak, training example:
The element.cs module

// Экземпляры этого класса будет содержать коллекция - UserCollection. 
    public class Element
    {
        // Поля.
 
        private string name;
        private int field1;
        private int field2;
 
        // Конструктор.
        public Element(string s, int a, int b)
        {
            name = s;
            field1 = a;
            field2 = b;
        }
 
        // Свойства.
 
        public int Field1
        {
            get { return field1; }
            set { field1 = value; }
        }
 
        public int Field2
        {
            get { return field2; }
            set { field2 = value; }
        }
 
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
    }

UserCollections.cs module
// Класс UserCollection коллекция (набор) объектов класса Element.
    // Для применения foreach, необходимо, чтобы класс реализовывал интерфейс - IEnumerable.
    public class UserCollection : IEnumerable, IEnumerator
    {
        public Element[] elementsArray = null;
 
        public UserCollection()
        {
            elementsArray = new Element[4];
            elementsArray[0] = new Element("A", 1, 10);
            elementsArray[1] = new Element("B", 2, 20);
            elementsArray[2] = new Element("C", 3, 30);
            elementsArray[3] = new Element("D", 4, 40);
        }
        
        // Указатель текущей позиции элемента в массиве.
        int position = -1;  
 
        // ------------------------------------------------------------------------------------------------------------------
        // Реализация интерфейса IEnumerator.
 
        // Передвинуть внутренний указатель (position) на одну позицию.
        public bool MoveNext()
        {
            if (position < elementsArray.Length - 1)
            {
                position++;
                return true;
            }
            else
            {
                return false;
            }
        }
 
        // Установить указатель (position) перед началом набора.
        public void Reset()
        {
            position = -1;
        }
 
        // Получить текущий элемент набора. 
        public object Current
        {
            get { return elementsArray[position]; }
        }
 
        // -----------------------------------------------------------------------------------------------------------------
        // Реализация интерфейса - IEnumerable.
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this as IEnumerator;
        }
    }

Program.cs module
UserCollection myCollection = new UserCollection();
         
            // Используем foreach, для обращения к каждому объекту Element внутри массива myCollection. 
            foreach (Element element in myCollection)
            {
                Console.WriteLine("Name: {0}  Field1: {1} Field2: {2}", element.Name, element.Field1, element.Field2);
            }
 
            //myCollection.Reset(); // Убрать комментарий для проверки.
 
            Console.Write(new string('-', 29) + "\n");
 
            // Используем foreach, для повторного обращения к каждому объекту Element внутри массива myCollection.
            foreach (Element element in myCollection)
            {
                Console.WriteLine("Name: {0}  Field1: {1} Field2: {2}", element.Name, element.Field1, element.Field2);
            }
 
            Console.Write(new string('-', 29) + "\n");

when working, the following is displayed on the screen: see Figure 1.
85a0e8e90b37430887ead429f6428362.JPG
If we remove the comment from the line (see Figure 2)
3e510de968554cd4899621dc324f40ff.JPGmyCollection.Reset();
- which resets the position pointer to -1, i.e. the limit of our collection, which makes it possible for another one the foreach loop will re-run through our collection and extract its elements, and then, at each iteration, placing each element in the iteration variable - displaying the value of the extracted collection element. And when the above line was commented out, then after the first foreach loop, the second one did not work - because the position pointer was already at the end of our collection.
After that, the author of the video course that I watch offers to consider the work of the foreach loop with an array and not with collections.
int [] array={1,2,3,4,5,6,7,8,9,10}
 
foreach(int titem in array)
{
Console.Writeline(item);
}

It then copies the given foreach loop, and does not insert a Reset() in between; - using two foreach loops "in a row" without a gap in the form of Reset();
The most interesting thing that the author says, and I quote - "Actually, all arrays in C# are real collections. There are no arrays in C# - it's just an illusion and convenient syntax."
Arguing this as follows:
public abstract class Array : ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable

that the Array class implements the IEnumerable interface - here on the forum I already asked about this interface Contents of the IEnumerable interface.
But here is my question: Why in "ordinary" collections - that is, which we create ourselves, you need to reset the position to re-pass through the collection using the foreach loop again, but in a regular array, which, according to the author's fame, is also a collection - we do not perform this action? (although I didn’t go deep into the structure of the Array class and I don’t know if there is a pointer in it at all? Maybe even the Array class implements the IEnumerable interface in it (in the class) there may not be such a mechanism - I don’t know)

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Andrew, 2016-09-10
@BadCats

You have confused concepts "collection" and "enumerator".
Typically, a collection holds the elements, and an enumerator a reference to the enumerable collection and its current position in it. Every time you use a standard collection (array, list, etc.) in foreach, a new enumerator is created. Therefore, there is no need to reset the state. You made a chimera out of a collection and its own enumerator ( return this as IEnumerator), so you have to manually reset the state.
This is very bad code. Solution - don't create your own collections at all. Since C# 2.0, there have been generic generic collections - List<T>, Dictionary<TKey, TValue>and many others that should be used.

N
Nikita, 2016-09-11
@interlocked

An enumerator is usually implemented in a private nested class, in whose methods you simply use an instance of your collection. To write a complete collection based on an array, you can use the generic interfaces ICollection<T>/ IList<T>. For dictionaries - IDictionary<TKey, TValue>etc. Disagree with the previous decision "don't create your own collections at all" - collections need to be created, standard ones are not always suitable for implementing some models. For example, for WPF there is ObservableCollection<T>, but what if you need a tracked dictionary?

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question