Как правильно заменить блок переключателей и Enum в этой ситуации (C #)? - PullRequest
5 голосов
/ 09 сентября 2011

Если это поможет, то следующий вопрос в контексте игры, которую я строю.

В нескольких разных местах у меня следующий сценарий. Существует родительский класс, для этого примера называется Skill, и у меня есть несколько подклассов, реализующих методы из родительского класса. Также существует другой родительский класс, который мы будем называть Vocation. Навыки должны быть перечислены в разных подклассах Vocation. Тем не менее, эти навыки должны быть доступны для всего в игре, которая использует любое задание.

Моя текущая установка состоит в том, чтобы иметь Enum с именем Skill.Id, так что Vocation содержит коллекцию значений из этого Enum, а когда сущность в игре получает это Vocation, коллекция передается в другой класс, называемый SkillFactory. Skill.Id требуется новая запись каждый раз, когда я создаю новый подкласс Skill, а также регистр в блоке переключателя для конструктора новых подклассов.

т.е:.

//Skill.Id
Enum{FireSkill,WaterSkill,etc}

//SkillFactory
public static Skill Create(Skill.Id id)
{
    switch(id)
    {
        case Skill.Id.FireSkill:
             return new FireSkill();
        //etc
    }
}

Это прекрасно работает, но использование enum и блока переключателей в качестве переходного периода выглядит как дополнительная нагрузка, чем мне нужно для решения этой проблемы. Есть ли более элегантный способ создания экземпляров этих подклассов навыков, но все же позволяет Vocation содержать коллекцию, идентифицирующую навыки, которые он может использовать?

Редактировать: Я прекрасно выбрасываю enum и связанный с ним блок переключателей, если Vocation может содержать коллекцию, которая позволяет произвольно создавать экземпляры подклассов Skill.

Ответы [ 2 ]

8 голосов
/ 09 сентября 2011

Вы можете сделать Dictionary<Skill.Id, Func<Skill>> и использовать его для создания экземпляра.

В конструкторе:

Dictionary<Skill.Id, Func<Skill>> creationMethods = new Dictionary<Skill.Id, Func<Skill>>();
public SkillFactory()
{
     creationMethods.Add(Skill.Id.FireSkill, () => new FireSkill());
     creationMethods.Add(Skill.Id.WaterSkill, () => new WaterSkill());
}

Тогда ваш метод Create становится:

public static Skill Create(Skill.Id id)
{
     return creationMethods[id]();
}

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

Это, как говорится, в конечном счете, полное избавление от перечисления может быть хорошим преимуществом для расширяемости. Однако это потребует более сложных изменений. Например, если вы использовали MEF, вы можете импортировать набор типов SkillFactory во время выполнения и связать их с именем (через метаданные) с помощью одного ImportMany. Это позволит вам добавлять новые подклассы навыков, не изменяя свою фабрику, и ссылаться на них по имени или какому-либо другому механизму.

0 голосов
/ 09 сентября 2011

, если эта функция создания будет использоваться так, что «case» будет приводить к накладным расходам, словарь с ключами enums будет генерировать много мусора.

В контексте игры xna это может быть хуже, чем "case".

«Если вы используете тип enum в качестве ключа словаря, внутренние словарные операции вызовут бокс. Вы можете избежать этого, используя целочисленные ключи и приведя значения enum к целым числам перед добавлением их в словарь». Извлечено отсюда

Вы можете использовать простой массив и привести enum к int для индексации:

 Enum {FireSkill=0,WaterSkill=1,etc}

 Func<Skill>[] CreationMethods = new Func<Skill>() 
 {
     () => new FireSkill(),
     () => new WaterSkill(),
 } 
...