none
Цикл foreach при работе с коллекциями и массивами RRS feed

  • Вопрос

  • Всем привет. Начал ищучение коллекцийи цикла foreach приминяемого для работы с ними.
    Имеется банальный, так сказать тренировачный пример:
    Модуль element.cs

    // Экземпляры этого класса будет содержать коллекция - 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

    // Класс 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

    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");

    при работе которго, на экран выводится следующие: см. рисунок 1

    Если же мы снимаем комментарий со строки(см рисунок 2)

    	
    
    myCollection.Reset();

    которая сбрасывает указатель position до -1 т.е ха пределлы нашей коллекции, что и дает возможность выванному после этого еще одному циклу foreach заново пробежатся по нашей коллекции и извлекая ее элементы, а затем при кажой итерации помещая каждый элемент в переменную итерации - выводить на экран значение извлеченного элемента коллекции. А когда вышеописаннная строка была закоментированна, то после первого цикла foreach, второй не срабатывал - т.к указатель position уже и так был в конце нашей коллекции.

    После этого автор видеокурса, который я смотрю предлагает рассмотреть работу цикла foreach вместе с массивом а не с коллекциями.

    int [] array={1,2,3,4,5,6,7,8,9,10}
     
    foreach(int titem in array)
    {
    Console.Writeline(item);
    }

    Затем копирует данный цикл foreach , и не вставляя между ними Reset(); - используя два цикла foreach "подряд" без промежутка в виде Reset();
    Самое интрестное, что автор говорит, цитирую - "На самом деле, все массывы в C# - являются настоящими коллекциями. В C# не существует массивов - это всего лишь иллюзия и удобный синтаксис."
    Аргументируя это вот чем:

    public abstract class Array : ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable
    что класс Array реалезует интерфейс IEnumerable - здесь же на форуме я уже спрашивал об этом интерфейсе Содержимое интерфейса IEnumerable.
    Но вот в чем мой вопрос:Почему в "обычных" коллекциях - т.е которые мы создаем сами нужно сбрасывать position для повторного проходп ро коллекции с помощью цикла foreach еще раз, а в обычном массиве, который по славам автора также является коллекцие - мы не производим этого действия? (правда в строение класса Array я не углублялся и не знаю, присутствует ли в нем вообще некий указатель? Может хоть и класс Array и реалезует интерфейс IEnumerable в нем (в классе) может не существовать подобного механизма - я не знаю)







    10 сентября 2016 г. 16:29

Ответы

  • Почему в "обычных" коллекциях - т.е которые мы создаем сами нужно сбрасывать position для повторного проходп ро коллекции с помощью цикла foreach еще раз, а в обычном массиве, который по славам автора также является коллекцие - мы не производим этого действия?
    Не нужно. Просто Ваша реализация интерфейса IEnumerable весьма "оригинальна". Большинство коллекций реализуют интерфейс IEnumerable таким образом, что два вызова GetEnumerator() возвращают два независимых друг от друга IEnumerator объекта. Вы же возвращаете один и тот же объект каждый раз. Более того, очень много реализаций интерфейса IEnumerator (в частности, все автоматически реализованные: yield return) метод Reset() просто не поддерживают и выбрасывают NotSupportedException при его вызове.
    public class UserCollection2 : IEnumerable
    {
        public Element[] elementsArray = null;
    
        public UserCollection2()
        {
            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);
        }
    
        // ------------------------------------------------------------------------------------------------------------------
        // Реализация интерфейса IEnumerator.
        private class Enumerator : IEnumerator {
    
            UserCollection2 collection;
    
            // Указатель текущей позиции элемента в массиве.
            int position = -1;
    
            public Enumerator(UserCollection2 collection) {
                this.collection=collection;
            }
    
            // Передвинуть внутренний указатель (position) на одну позицию.
            public bool MoveNext()
            {
                if (position < collection.elementsArray.Length - 1)
                {
                    position++;
                    return true;
                }
                else
                {
                    return false;
                }
            }
    
            public void Reset()
            {
                throw new NotSupportedException();
            }
    
            // Получить текущий элемент набора. 
            public object Current
            {
                get { return collection.elementsArray[position]; }
            }
        }
    
        // -----------------------------------------------------------------------------------------------------------------
        // Реализация интерфейса - IEnumerable.
        IEnumerator IEnumerable.GetEnumerator()
        {
            return new Enumerator(this);
        }
    }
    С такой реализацией Вы легко можете перечислять одну и туже коллекцию в двух вложенных циклах, чего будет весьма проблематично добиться даже при использовании Reset() в Вашем коде:
    UserCollection2 myCollection = new UserCollection2();
    
    foreach (Element element1 in myCollection)
    {
        foreach (Element element2 in myCollection)
        {
            Console.WriteLine("Name: {0} Field1: {1} Field2: {2}|Name: {3} Field1: {4} Field2: {5}", element1.Name, element1.Field1, element1.Field2,
                                                                                                     element2.Name, element2.Field1, element2.Field2);
        }
    }
    10 сентября 2016 г. 19:18