Циклическое наполнение/удаление элементов массива

kodges

Суть вопроса в следующем. Есть массив размером в 21 элемент. При каждой итерации в массив заносится новый элемент начиная с 0 и до 21. Как только все индексы массива становятся заняты и добавлять новый уже некуда, из массива удаляется самый первый элемент, и добавляется новый элемент в конец массива. И так бесконечно... OnTick() выполняется каждую секунду. Но почему то через 21 итерацию выдается ошибка: Crashed in OnTick with IndexOutOfRangeException: Индекс находился вне границ массива.Что я делаю не так ? Помогите пожалуйста. Я новичок в C#. Вот мой код:
int k = -1, maxTick  =  21;
******[] ticks = new ******[21];
 
        protected override void OnTick()
        {
            ****** Bid = Symbol.Bid;
            ****** Ask = Symbol.Ask;
            ****** Point = Symbol.TickSize;
 
               k++;
               ticks[k] = Bid;
 
             if (k == maxTick - 1)
             {
                k--;
                ticks = Delete(ticks.ToList(), 0);
             }
        }
 
        static ******[] Delete(List<******> array, int indexToDelete)
        {
            array.RemoveAt(indexToDelete);
            return array.ToArray();
        }
14 ответов

kodges

array.RemoveAt(indexToDelete);
return array.ToArray();
Задумайтесь над этим кодом. Получается что то типа задачки для начальной школы: Есть список элементов из 21 элемента. Сколько элементов будет в массиве, созданном на основе этого списка, если из него удалить 1 элемент?


kodges

А почему бы не использовать очередь FIFO?


kodges

Задумайтесь над этим кодом. Получается что то типа задачки для начальной школы: Есть список элементов из 21 элемента. Сколько элементов будет в массиве, созданном на основе этого списка, если из него удалить 1 элемент?
Все понял, у меня после удаления первого элемента новый массив получается размером уже не 21 а 20 элементов. Подскажите как добавить новый 21-ый пустой элемент в новый массив. Или хотя бы подскажите где про это почитать. Просто с C# я знаком буквально пару дней, и пытаюсь соорудить нужный мне функционал находя в сети куски кода.
А почему бы не использовать очередь FIFO?
Ну во первых я не знаю что это такое. Если это проще, быстрее, занимает меньше кода и годится для моей задачи, то ссылку на пример дайте пожалуйста, или пример кода который решает мою задачу.


kodges

Как-то так, если совсем просто. Последний цикл while можно убрать, если не предполагается извлекать из коллекции последние добавленные элементы. В первом цикле идет добавление до 21 элемента, как только количество элементов ==21, то один извлекается, в сл. итерации еще один добавляется в начало коллекции и так по кругу, до конца счетчика.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ConsoleApplication16
{
    class Program
    {
        static void Main(string[] args)
        {
            Queue<int> Q = new Queue<int>(21);
            int Item;
 
            for (int i = 0; i < 10000; i++)
            {
                Q.Enqueue(i);
                if (Q.Count == 21)
                {
                    Item = Q.Dequeue();
                    Console.WriteLine("{0}", Item);
                }
            }
 
            while (Q.Count > 0)
            {
                Item = Q.Dequeue();
                Console.WriteLine("{0}", Item);
            }
            Console.Read();
        }
    }
}


kodges

Все оказалось просто. Нужно было после
ticks = Delete(ticks.ToList(), 0);
добавить строку
Array.Resize(ref ticks, ticks.Length + 1);
Тем самым изменить размер нового массива до первоначального.Большое спасибо всем за помощь!Еще вопрос по массивам. Попробовал задать размер массива из переменной, но выдается ошибка: Инициализатор поля не может обращаться к нестатическому полю, методу или свойству.
int cnt = 21;
******[] ticks = new ******[cnt ];
Если дописать переменной static
static int cnt = 21;
******[] ticks = new ******[cnt ];
тогда выдается ошибка: Индекс находился вне границ массива.Но почему? Ведь если я прописываю размер массива цифрой как было изначально то все работает.


kodges

Если дописать переменной static Код C#1 2 static int cnt = 21; ******[] ticks = new ******[cnt ];тогда выдается ошибка: Индекс находился вне границ массива.
Данное исключение не генерируется при создании массива. Оно генерируется только при обращении, если индекс не входит в границы массива. Поэтому ошибка возникает в другом месте вашего кода. Если покажете, какой код у вас в итоге получился, то и ошибку будет найти легче.PS: Если у вас в задании жестко не прописано, что нужно использовать массив, то используйте очередь (Queue) - она специально для подобных целей и предназначена. Пример кода insite2012 уже привел.


kodges

PS: Если у вас в задании жестко не прописано, что нужно использовать массив, то используйте очередь (Queue) - она специально для подобных целей и предназначена.
Полностью согласен. Понятно, что можно и FIFO, и LIFO на основе массива реализовать, но зачем изобретать колесо, если оно уже есть и катится, надо только его толкнуть...


kodges

Спасибо за помощь, с ошибкой последней разобрался. Буду думать дальше что почем...


kodges

Подскажите, а как использовать очередь Queue если мне надо заносить в очередь элементы в виде индекс => значение, причем индексом будет являться текущая метка времени Unix Timestamp. И потом по этим индексам необходимо будет извлекать значения. Что-то типа вывести значение если метка времени индекса больше или равна текущей метке времени минус 60 секунд. Можно пример такой реализации если не затруднит? Мне нужно увидеть как именно добавлять это в очередь Queue и как извлекать по индексу потом.Да и новый элемент "индекс => значение" должно заноситься в конец очереди, а не в начало.


kodges

kodges, не знаю особенности реализации очереди в .NET Framework, но в теории из очереди можно извелкать только тот элемент, который находится там дольше всего. Видимо, очередь вам не особо подходит. Массив для ваших целей тоже категорически не подходит. Либо вы будете устраивать такую чехарду с памятью, как это есть у вас сейчас, либо вам прийдется при добавлении нового элемента в цикле смещать все имеющийся на один к началу массива, что тоже не особо эффективно. Используйте тогда обычный список (List).


kodges

Queue предназначен для хранения элементов, а не пар ключ-значение. Как вариант, реализовать свой класс с нужными значениями и очередь нужного типа (тип очереди - наш класс), дальше добавляем - извлекаем, как выше показано.Походит там очередь, если в самом начале правильно стояло условие. Очередь - это FIFO (First In, First Out), первым вошел, первым вышел.Вот пример.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ConsoleApplication22
{
    class Program
    {
        static void Main(string[] args)
        {
            Queue<MyClass> Q = new Queue<MyClass>();
 
            for (int i = 0; i < 100; i++)
            {
                var mc=new MyClass(i.ToString("d4"),1,2);
                Q.Enqueue(mc);
 
                if (Q.Count == 21)
                {
                    var item = Q.Dequeue();
                    Console.WriteLine("ID: {0}, X: {1}, Y: {2}", item.ID, item.X, item.Y);
                }
            }
 
            while (Q.Count > 0)
            {
                var item = Q.Dequeue();
                Console.WriteLine("ID: {0}, X: {1}, Y: {2}", item.ID, item.X, item.Y);
            }
 
            Console.Read();
        }
    }
    class MyClass
    {
        public MyClass(string id, int x, int y)
        {
            ID = id;
            X = x;
            Y = y;
        }
        public string ID
        {
            get;
            private set;
        }
        public int X
        {
            get;
            private set;
        }
        public int Y
        {
            get;
            private set;
        }
    }
}


kodges

Попробовал реализовать решение списком List.
int maxTime = 20;
SortedList<int, ******> list = new SortedList<int, ******>();
 
        protected override void OnTick()
        {
            ****** Bid = 3.14;
            int Time = UnixTimeNow();
 
            if (list.ContainsKey(Time) != true)
            {
                list.Add(Time, Bid);
            }
 
            ICollection<int> keys = list.Keys;
 
            foreach (int s in keys)
            {
                if (Time - maxTime > s)
                {
                    // list.Remove(s);
                }
            }
        }
 
        static int UnixTimeNow()
        {
            TimeSpan _TimeSpan = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0));
            return (int)Math.Floor(_TimeSpan.TotalSeconds);
        }
Метод OnTick() выполняется примерно раз в секунду, когда чаще когда реже... На первый взгляд все просто. Работает добавление в список, все ок. Но если раскоментировать строку: list.Remove(s); То вылетает с ошибкой: "Коллекция была изменена после создания экземпляра перечислителя". Помогите как решить проблему, я уже голову сломал. А мне надо удалять ненужные элементы из списка чтобы он не разростался бесконечно.


kodges

kodges, у foreach есть свои ограничения, с которыми вы познакомились. Замените цикл на for.


kodges

kodges, у foreach есть свои ограничения, с которыми вы познакомились. Замените цикл на for.
Но как в цикле for получить ключ и значение списка? Если я делаю так:
            for (int s = 0; s < keys.Count; s++)
            {
                Print(s);
            }
То печатается не ключ как в случае с foreach, а видимо просто индекс, так как идет просто нумерация от 0.