среда, 13 июля 2011 г.

Компромисс в выборе между классом и структурой

На тему навела вот эта статья "NET 4.0: Class vs Struct или в чём различия между Классом и Структурой" . Давно уже собирался собрать для себя набор иерархии универсальных коллекций, которые не попадают под сборку мусора. Тема в очередной раз стала интересна после моего известного эксперимента с Windows Phone 7. Очень сомневаюсь, что моя статья будет полезна для бизнес-приложений.  Меня больше интересует данная тема для использования в XNA/DirectX и как область применения - игры.

Приглашаю вас обсудить данный вопрос.

И так, условия задачи:
- Нужна коллекция элементов для описания, например, системы частиц (это может быть все что угодно). Такая штука в которой в среднем 60 раз в секунду добавляются и удаляются сотни элементов.
- Изменения в коллекции не должны влиять на сборку мусора.
- Нельзя использовать структуры! Только ссылочные типы.
- Запрещено создавать и удалять классы, за исключением первой инициализации.
- Физическое количество элементов в коллекции должно быть постоянным. Т.к. изменение длинны массива достаточно накладная операция, а количество элементов должно постоянно плавать, то подойдет подобие кеша. Ограничение - разработчику желательно четко знать максимально возможное количество элементов в такой коллекции. А это значит игра должна быть грамотно спроектирована. Хотя лазейку я оставлю.


Набросал вот такой черновик.

Базовый класс для элемента коллекции:

public class TElementConstLength
    {
        private  int indexInCollection = -1;
        internal int IndexInCollection { get { return indexInCollection; } }
        internal void SetIndexInCollection(int i)
        {
            indexInCollection = i;
        }
    }


Базовый класс для коллекции:

public class TCollectionConstLength<T> where T : TElementConstLength, new()
    {
        private T[] elements;

        private int count = 0;
        /// <summary>
        /// Количество живых элементов
        /// </summary>
        public  int Count { get { return count; } }

        private int end = -1;

        /// <summary>
        /// Инициализация колекции
        /// </summary>
        /// <param name="length">Длинна коллекции</param>
        public TCollectionConstLength(int length)
        {
            elements = new T[length];
        }

        /// <summary>
        /// Доступ к элементу по индексу
        /// </summary>
        /// <param name="index">Индекс</param>
        /// <returns>Элемент</returns>
        public T this[int index]
        {
            get { return elements[index]; }
        }

        /// <summary>
        /// Выделение места под новый элемент
        /// </summary>
        /// <returns>Новый элемент</returns>
        public virtual T GetNew() // = Add
        {
            // если список полон, для режима отладки
            if(end == elements.Length - 1)
                Array.Resize(ref elements, elements.Length + 1);
            // 
            end++;
            count++;
            // если не инициализирован элемент
            if(elements[end] == null)
            {
                elements[end] = new T();
                elements[end].SetIndexInCollection(end);
            }
            //
            return elements[end];
        }

        /// <summary>
        /// Очистка списка
        /// </summary>
        public void Clear()
        {
            end = -1;
            count = 0;
        }

        /// <summary>
        /// Удаление элемента по индексу
        /// </summary>
        /// <param name="index">Индекс</param>
        public virtual void Remove(int index)
        {
            // выход за пределы
            if(index < 0 || index > end)
                return;
            if(index < end)
            {
                // рокировка элементов
                T temp = elements[end];
                elements[end] = elements[index];
                elements[index] = temp;
                // рокировка индексов
                int i = elements[end].IndexInCollection;
                elements[end].SetIndexInCollection(elements[index].IndexInCollection);
                elements[index].SetIndexInCollection(i);
            }
            // уменьшаем список
            end--;
            count--;
        }

        /// <summary>
        /// Удаление элемента
        /// </summary>
        /// <param name="e">Элемент</param>
        public virtual void Remove(T e)
        {
            // проверка на соответствие ссылок (защита от тупости)
            if(e != elements[e.IndexInCollection])
                return;
            Remove(e.IndexInCollection);
        }
    }

Вот пример для тестов:

using System;

namespace CollectionConstLength
{

    public class ElementTest : TElementConstLength
    {
        private string name;
        public string Name { get { return name; } set { name = value; } }
        public ElementTest() { }
    }

    class Program
    {

        static TCollectionConstLength<ElementTest> item;

        static void Main(string[] args)
        {

            item = new TCollectionConstLength<ElementTest>(5);
            ElementTest et;
            for(int i = 0; i < 5; i++)
            {
                et = item.GetNew();
                et.Name = i.ToString();
            }
            //
            Print();
            //
            item.Remove(item[1]);
            Print();
            //
            Console.ReadKey();
        }

        static void Print()
        {
            Console.WriteLine("//--------------------------------");
            for(int i = 0; i < item.Count; i++)
            {
                Console.WriteLine(
                    string.Format("index - {0}; name - \"{1}\";", 
                        item[i].IndexInCollection, 
                        item[i].Name));
            }
            Console.WriteLine("//--------------------------------");
        }
    }
}


Пример наращивания функционала через наследование:

- Расширенный класс элемента:

public class TUpdate : TElementConstLength
    {
        public virtual void Update() { }
    }

- Расширенный класс коллекции

public class TUpdateCollection<T> : TCollectionConstLength<T> where T : TUpdate, new()
    {
        public TUpdateCollection(int l) : base(l) { }
        public virtual void Update()
        {
            for(int i = 0; i < base.Count; i++)
            {
                base[i].Update();
            }
        }
    }


Данный подход позволяет исключить сборку мусора и равномерно распределить нагрузку на процессор. Для каждого наследника от базового класса нужно добавить метод заполняющий поля данного класса, что делает процесс инициализации нового члена коллекции похожим на инициализацию структуры.

Давайте пообщаемся на эту тему.
Так же буду рад ссылкам на статьи с подобными темами и изысканиями.

суббота, 2 июля 2011 г.

Лето ... Юмор ...


Бред конечно, но когда пытаешься сосредоточиться на работе, а тебе при этом постоянно мешают, еще эта жара - именно такие мысли лезут в голову:

Тенгизи: к сожалению, пока некогда :(( работа... осенью закуплю девайсы и морозными зимними вечерами... :)
Dmitry: да.. да..)))
Dmitry: я вот тут думаю, как бы до морозных вечеров обороты набрать, что бы потом только щепки летели)))
Тенгизи: лето... мозги вообще ничего делать не хотят... :((
Тенгизи: у нас жара вторую неделю... жесть...
Dmitry: ищу стимулы, у нас тоже пекло)
Тенгизи: ну у вас-то еще бы!
Dmitry: )))
Тенгизи: мы-то люди северные, к жаре не привычные
Dmitry: да я лето из за лени ненавижу)))
Тенгизи: хотя я 17 лет в минводах прожил, но тут уже адаптировался... )
Dmitry: у меня такое подозрение, что для того что бы я смог раскрыть все свои способности, по максимуму, и реализовать их - меня нужно запереть на какой ни будь полярной станции и не выпускать.
Dmitry: только с компом и интернетом)))
Dmitry: и раз в неделю привозить еду и женщин)))))))))))
Dmitry: ух ...
Тенгизи: у меня такое же мнение!!! сто пудово! если запереть - реализуемся...
Тенгизи: даже женщин на хрен не возите, только отвлекать будут... потерплю тройку месяцев ))))
Dmitry: раз в неделю можно часок расслабиться)))
Тенгизи: при таком раскладе даже в покер научится играть можно профессионально... ))
Тенгизи: ну.. если часок... но потом чтоб СРАЗУ УВОЗИЛИ! :))
Dmitry: прилетела на вертолете, еды привезла, удовлетворила и улетела)))
Dmitry: идеал женщины)))
Тенгизи: )))))))
Тенгизи: к тому же чтоб глухонемая была ))
Dmitry: не вопрос)))

Приятно осознавать, что ты не один такой :) 

"GB&W" in "Top WP7 Games : June 2011 [ POLL ]"

Сайт bestwp7games.com предложил голосование за игры для Windows Phone 7 вышедшие в июне. В этом списке на ряду с топовыми играми, имеющими статут xbox live, каким то образом, оказалась и наша игра GB&W. Приглашаю вас присоединиться к нашей группе болельщиков и принять участие в голосовании.

Проголосовать можно тут

Регистрация не нужна. Достаточно выбрать соответствующий пункт и подтвердить кнопкой "vote".

Всем огромное спасибо за отклик и участие!

P.S. Топовым играм, имеющим статус xbox live и команду поддержки, хватает внимания, загрузок и покупок, а вот нам бы все это даже очень не помешало.