Как сделать «статически перегруженную константу» в C #?

#c# #polymorphism

#c# #полиморфизм

Вопрос:

Я создаю игру на C #. Каждый уровень состоит из нескольких плиток. Каждая плитка имеет определенный тип, например, травяной пол, деревянная стена и т.д.

В идеале я хотел бы иметь один базовый класс «Tile» и наследовать от него для создания классов для каждого типа tile. Я хотел бы, чтобы свойства плитки были каким-то статическим / const / etc членом подкласса, поскольку каждый тип плитки всегда будет иметь одни и те же свойства. Т.Е. я не хочу, чтобы у 100 плиток было свойство, все из которых имеют одинаковое значение, что кажется довольно неэффективным.

Проблема в том, что вы не можете этого сделать в C #. Есть ли какие-либо способы достичь того, чего я хочу?

Моей альтернативной идеей было разделить все это на деревья, в одном из которых был бы только класс «Tile», представляющий экземпляр, и другой «TileType», из которого я создаю экземпляр одного объекта для каждого типа и, возможно, получаю к ним доступ через какой-то «TileTypeCollection». Хотя это кажется странным, я бы предпочел сделать это первым способом.

Существуют ли какие-либо общие рекомендации при работе с подобной ситуацией?

Комментарии:

1. В OO-программировании целью расширения базового типа является изменение или добавление поведения, а не изменение свойств / атрибутов. В вашем примере требуется только один класс Tile — конечно, могут быть разные типы tile, но это всего лишь атрибуты Tile. Допустим, ваш тип плитки имеет виртуальный метод PlayerEntered() , у вас может быть производный класс плитки с именем BlockingTile , который переопределит PlayerEnetered() и остановит проигрыватель, или у вас может быть класс BurningTile , который переопределяет PlayerEntered() и запускает проигрыватель..

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

3. На слегка касательном примечании, если у вас есть 100 фрагментов, для каждого из которых установлено свойство с постоянной строкой «grass», компилятор будет иметь все ссылки, указывающие на один экземпляр, вместо создания 100 экземпляров String. Это означает, что вы используете только 4 (или 8) байта на плитку для ссылки, так что это далеко не так плохо, как вы думаете.

4. Это именно то, для чего разработан шаблон flyweight, как предлагает Марино Симич 🙂 Действительно ли каждому тайлу нужно строковое свойство для имени текстуры? Это то, что делает тайл уникальным? Нет… Я думаю, что тайлу в этом случае нужны только два свойства: позиция в игровом мире и идентификатор типа тайла… именно там будут использоваться свойства типа плитки (т. Е. текстуры).

Ответ №1:

Вы ищете шаблон проектирования с минимальным весом:

http://en.wikipedia.org/wiki/Flyweight_pattern

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

введите описание изображения здесь

Здесь есть несколько примеров C #:http://www.dofactory.com/Patterns/PatternFlyweight.aspx#_self2

Комментарии:

1. Это кажется лучшим решением. Большое спасибо.

2. Кроме того, возможно, у вас есть какой-либо опыт работы с XNA Framework? Если да, можете ли вы сказать, что импортер контента использует этот шаблон?

Ответ №2:

Вы можете создать структуру класса следующим образом:

 public abstract class Tile
{
  public abstract string BaseType;
}

public class Floor : Tile
{
  public override string BaseType
  {
    get
    {
       return "floor";
    }
  }
}

public class Grass : Tile
{
  public override string BaseType
  {
    get
    {
       return "grass";
    }
  }
}

public class Wooden : Tile
{
  public override string BaseType
  {
    get
    {
       return "wooden";
    }
  }
}
  

Комментарии:

1. Это полное злоупотребление полиморфизмом. Смысл расширения типа заключается в добавлении / изменении поведения , а не в предоставлении разных данных .

2. @MattDavey: Спасибо, что указали на это. На самом деле я хотел прокомментировать это решение, поскольку я изучал C , и недавно, когда я хотел добиться того же в C #, я сделал это вот так. Можете ли вы указать мне на решение, которое вам нравится из приведенных здесь. Итак, я бы знал, как думать об этом в C #.

3. по сути, в OO-программировании объект состоит из двух элементов — атрибутов (properties) и поведения (methods). Вы можете изменять атрибуты объекта в любое время, но для добавления или изменения поведения требуется полиморфизм (не выдерживающий других шаблонов, например. Декоратор). Если вы видите мой комментарий в исходном вопросе, я привел пример того, когда полиморфизм может быть необходим 🙂

Ответ №3:

Разве вы не можете просто использовать статическое поле для резервного копирования свойства в вашем базовом классе Tile?

 public abstract class Tile
{
  private static string _commonProperty

  public static string CommonProperty
  {
    get { return _commonProperty; }
    set { _commonProperty = value; }
  }
}
  

Комментарии:

1. Тогда свойство также должно быть статическим.

2. Не обязательно — может быть полезно иметь свойство, которое возвращает общее значение. (Когда вы реализуете интерфейс, имейте свойство с значением по умолчанию, но переопределяемым, наследуемое от класса или подготавливающее этот класс к наследованию …)

3. @Jean Hominal: но это свойство будет возвращать значение, общее для всех дочерних классов этого класса, и, насколько я понимаю, вопрос хочет возвращать разное его значение для каждого дочернего класса.

Ответ №4:

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

Ответ №5:

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

ЕСЛИ существует общее свойство, значение которого отличается для каждого типа, то объявите его в базовом классе, сделайте его значением только для чтения и задайте его в конструкторе каждого типа.

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