Свяжите значение с подклассом в c # - PullRequest
2 голосов
/ 23 июля 2010

У меня есть следующий класс, и мой вопрос заключается в его функции GetComponent

class GameObject
{ 

    private List<GameComponent> _components = new List<GameComponent>();
    public void AddComponent(GameComponent component)
    void RemoveComponent(string name)
    public T GetComponent<T>(string name) where T : GameComponent

}

class GameComponent
{
    public string Name { get; set; }
}

Итак, в GetComponent я хочу получить компонент, обозначенный name, и вернуть его как T.

Так что я бы назвал
NavigationComponent navigation = gameObject.GetComponent<NavigationComponent>("navigation");
где NavigationComponent будет подклассом GameComponent

Поскольку каждый экземпляр NavigationComponent всегда будет иметь одно и то же имя, «навигация», я размышляю, могу ли я что-то сделать, чтобы упростить GetComponent до следующего

NavigationComponent navigation = gameObject.GetComponent<NavigationComponent>();

Заранее спасибо.

Обновление 1

Хорошо, я должен отметить, что GameObject работает как набор GameObject, идентифицируемых по их имени, поэтому в нем может быть только один из них.

Итак, чтобы уточнить, скажем, у нас есть
class SimpleNavigation : GameComponent {...}
class AdvancedNavigation : GameComponent {...}
class SensorSystem : GameComponent {...}

Тогда я бы дал SimpleNavigation и AdvancedNavigation имя "навигация", а для SensorSystem имя "датчик"

Так что у меня не может быть и SimpleNavigation, и AdvancedNavigation в приведенном выше примере.

Надеюсь, что это имеет смысл.

Заранее спасибо.

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

Ответы [ 3 ]

1 голос
/ 23 июля 2010

Во-первых, если все компоненты NavigationComponents будут иметь одинаковые имена, как вы будете различать их? Если вы хотите вернуть только один NavigationComponent, вам потребуется уникальный идентификатор.

Вы можете использовать метод OfType () для возврата всех экземпляров типа:

var navigationComponents = _components.OfType<NavigationComponent>();

Оттуда вы можете выбрать один компонент NavigationComponent по уникальному идентификатору:

NavigationComponent nc = navigationComponents.Where(n => n.Id == 1);

1 голос
/ 23 июля 2010

Обновление : В ответ на ваши изменения:

Тогда я бы дал SimpleNavigation и AdvancedNavigation имя "навигация", а системе SensorS - имя "сенсор"

Если это так, то вы действительно не можете надеяться на такой интерфейс ...

public T GetComponent<T>() where T : GameComponent { }

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

Почему бы не использовать вместо enum?Тогда у вас может быть что-то вроде этого:

public enum ComponentType
{
    Navigation,
    Sensor // etc.
}

И ваш класс GameObject может в свою очередь содержать Dictionary<ComponentType, GameComponent>.

Тогда ваш GetComponent методможет выглядеть так:

GameComponent GetComponent(ComponentType type) { }

В любом случае, это мысль ...


Я думаю, что имеет больше смысла, это:

Во-первых, если выищите по имени, используйте Dictionary<string, GameComponent> вместо List<GameComponent>.Это даст вам O (1) время поиска вместо O (N).

Теперь, если вы хотите, чтобы имя зависело от типа, определите свой класс GameComponent следующим образом:

class GameComponent
{
    public virtual string Name
    {
        // You could also hard-code this or cache it
        // for better performance.
        get { return this.GetType().Name; }
    }
}

Затем вы можете переопределить свойство Name в ваших производных методах, например:

class NavigationComponent : GameComponent
{
    public override string Name
    {
        // This returns the same thing as GetType().Name,
        // but it's faster.
        get { return "NavigationComponent"; }
    }
}

Тогда ваш метод AddComponent будет иметь вид:

public void AddComponent(GameComponent component)
{
    _components.Add(component.Name, component);
}

иВаш GetComponent метод будет простым:

public T GetComponent<T>() where T : GameComponent
{
    return _components[typeof(T).Name] as T;
}
1 голос
/ 23 июля 2010

Переписано:

Вы упомянули, что .Name должен оставаться в качестве свойства в вашем GameComponent классе - поэтому один из вариантов, который следует рассмотреть, это переопределение оператора равенства, чтобы сказать«если два экземпляра GameComponent имеют одинаковые имена, их следует считать равными».В сочетании с предложением Дейва об использовании OfType() у вас будет что-то вроде этого:

class GameObject
{ 

  private List<GameComponent> _components = new List<GameComponent>();

  public T GetComponent<T>(string name) where T : GameComponent, new
  {
    return
      (from c in _components.OfType<T>()
      where c == new T { Name = name }
      select c).FirstOrDefault();
  }
} 

class GameComponent
{
  public string Name { get; set; }

  public bool Equals(GameComponent other)
  {
    if (ReferenceEquals(null, other))
    {
      return false;
    }
    if (ReferenceEquals(this, other))
    {
      return true;
    }
    return Equals(other.Name, Name);
  }

  public override bool Equals(object obj)
  {
    if (ReferenceEquals(null, obj))
    {
      return false;
    }
    if (ReferenceEquals(this, obj))
    {
      return true;
    }
    if (obj.GetType() != typeof(GameComponent))
    {
      return false;
    }
    return Equals((GameComponent)obj);
  }

  public override int GetHashCode()
  {
    return (Name != null ? Name.GetHashCode() : 0);
  }
}

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

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