Как бороться с неизменностью возвращаемых структур?

Я пишу игру, в которой есть огромный 2D-массив «ячеек». Ячейка занимает всего 3 байта. У меня также есть класс CellMap, который содержит 2D-массив в качестве частного поля и обеспечивает доступ к нему через открытый индексатор.

Профилирование показало, что проблема с производительностью вызвана сборкой мусора слишком большого количества объектов Cell. Поэтому я решил сделать Cell структурой (это был класс).

Но теперь такой код не работает:

cellMap[x, y].Population++;

Я могу придумать много вариантов, но мне не очень нравится ни один из них.

  1. Сделайте массив общедоступным и напишите cellMap.Data [x, y] .Population = 5;
  2. Прекратите использование класса CellMap и просто напрямую используйте 2D-массив. Но CellMap очень удобен, потому что он реализует свою собственную оптимизированную сериализацию и предоставляет свойства Width и Height , которые более удобны, чем запись cellMap.GetLength (0)
  3. Сделайте ячейку неизменной . Но тогда как будет выглядеть код? cellMap [x, y] = IncrementCellPopulation (cellMap [x, y]) ? Очень многословно.
  4. Пара служебных функций , таких как cellMap.SetPopulationAt (x, y, 5)
  5. В каждом классе, который владеет CellMap, добавьте свойство утилиты наподобие private Cell [,] CellData {get {return this.CellMap.GetInternalArray (); }} , поэтому мой код может выглядеть следующим образом CellData [x, y] .Population ++

Как традиционно решается эта проблема?

8
задан Stefan Monov 15 July 2011 в 19:15
поделиться

2 ответа

Если ваша карта ячеек на самом деле «разреженная», то есть если есть много соседних ячеек, которые либо не имеют значения, либо имеют какое-то значение по умолчанию, я бы посоветовал вам не создавать для них объект ячейки. Создавайте объекты только для ячеек, которые на самом деле имеют какое-либо состояние, отличное от значения по умолчанию. (Это может значительно уменьшить общее количество ячеек, тем самым разгрузив сборщик мусора.)

Этот подход, конечно, потребует от вас найти новый способ хранения вашей карты ячеек. Вам придется отказаться от хранения ваших ячеек в массиве (поскольку они не разрежены) и использовать другой тип структуры данных, возможно, дерево.

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

Такая схема позволит вам хранить только те ячейки, которые вам нужны, и в то же время обеспечит быстрый доступ к любой ячейке на вашей карте. Если в деревьях не найдена ячейка с заданными координатами, можно предположить, что это ячейка по умолчанию.

2
ответ дан 5 December 2019 в 05:07
поделиться

Если вы хотите сделать Cell неизменяемым (что и следует делать, если это структура), то хорошим методом является создание фабрики, которая является методом экземпляра в Cell :

struct C
{
    public int Foo { get; private set; }
    public int Bar { get; private set; }
    private C (int foo, int bar) : this()
    {
        this.Foo = foo;
        this.Bar = bar;
    }
    public static C Empty = default(C);
    public C WithFoo(int foo)
    {
        return new C(foo, this.Bar);
    }
    public C WithBar(int bar)
    {
        return new C(this.Foo, bar);
    }
    public C IncrementFoo()
    {
        return new C(this.Foo + 1, bar);
    }
    // etc
}
...
C c = C.Empty;
c = c.WithFoo(10);
c = c.WithBar(20);
c = c.IncrementFoo();
// c is now 11, 20

Таким образом, ваш код будет примерно таким

map[x,y] = map[x,y].IncrementPopulation();

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

9
ответ дан 5 December 2019 в 05:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: