【问题标题】:Associate a value with a subclass in c#将值与 C# 中的子类关联
【发布时间】:2011-03-20 04:43:24
【问题描述】:

我有以下课程,我的问题在于它的 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&lt;NavigationComponent&gt;("navigation");
其中 NavigationComponent 将是 GameComponent 的子类

由于每个 NavigationComponent 实例只会具有相同的名称“导航”,我正在考虑是否可以做一些事情来将 GetComponent 简化为以下内容

NavigationComponent navigation = gameObject.GetComponent&lt;NavigationComponent&gt;();

提前致谢。

更新 1

我应该提一下,GameObject 的工作方式类似于一组 GameObject,由它们的名称标识,因此其中每个只能有一个。

所以要详细说明我们有
class SimpleNavigation : GameComponent {...}
class AdvancedNavigation : GameComponent {...}
class SensorSystem : GameComponent {...}

然后我会给 SimpleNavigation 和 AdvancedNavigation 命名为“navigation”,给 SensorSystem 命名为“sensor”

所以我不能在上面的例子中同时拥有 SimpleNavigation 和 AdvancedNavigation。

希望这是有道理的。

提前致谢。

附言我担心当没有太多收获和长期规范可能会改变时,我可能会设置太多约束,但这超出了我猜的问题范围

【问题讨论】:

  • 你听说过依赖注入(IoC:控制反转)吗?
  • 所以您想从GameComponent 的列表中获取NavigationComponent
  • 不,我没有,我现在去查一下
  • @GenericTypeTea 是的,这是正确的
  • 你使用的是什么版本的框架?

标签: c# components


【解决方案1】:

更新:回应您的修改:

然后我会给 SimpleNavigation 和 高级导航名称 “导航”和到 SensorSystem 名称“传感器”

如果是这样的话,那你真的不能指望有这样的界面了……

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

...因为多个GameComponent 对象类型可能共享相同的名称。所以你不能从类型推断名称。

为什么不改用enum 呢?那么你可能会有这样的事情:

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

而您的GameObject 类又可以持有Dictionary&lt;ComponentType, GameComponent&gt;

那么您的GetComponent 方法可能如下所示:

GameComponent GetComponent(ComponentType type) { }

无论如何,这是一个想法......


我认为更有意义的是:

首先,如果您按名称查找,请使用Dictionary&lt;string, GameComponent&gt; 而不是List&lt;GameComponent&gt;。这将为您提供 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;
}

【讨论】:

  • 我还没有输入你的代码,但是如果它不是静态的,我怎么能在类型上调用 .Name 呢?
  • @Xtapodi:调用this.GetType().Name 将返回对象的type 名称(本身是System.Type 对象)。这通常没有意义,除非您已指定您实际上希望 GameComponent 对象的名称与其类型的名称相同。所以出于这个原因,Type.Name 属性正是您想要的。
  • 感谢您的新更新。如果像虚拟静态方法这样的东西是可能的,那么我想我可以做到,我希望某些解决方案可能存在属性,它可能存在,但我想把它放在那里会是相当昂贵的性能明智的,我不会无论如何都能强制执行。但我从未使用过属性,所以我可能是错的。我可以强制 GameObject 子类具有 [ComponentType(string name)] 属性吗?
【解决方案2】:

重写:

您已经提到 .Name 应该 作为属性保留在您的 GameComponent 类中——因此要考虑的一个选项是覆盖相等运算符以说“如果 @987654324 的两个实例@ 具有相同的名称,那么它们应该被视为相等”。结合使用OfType()Dave's suggestion,你会得到这样的结果:

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 时,X 和 Y 应该被认为是相等的”——而等式重载让您可以在单个集中式主页(就在实体上)实现该逻辑。

【讨论】:

  • 我已经实现了上面的方法,带有 FirstOrDefault 的 GetComponent 和一个 lamda,但我认为我不需要展示它,因为我真的想改变它。在第一个注释中,因为我想要拥有可能具有 NavigationComponent 和 SensesComponent 等的 GameObjects。在第二个注释中,为了避免从我调用它的地方投射它,我想调用它的代码行数更少。
  • 啊,我不明白你的问题的意图。我已经用另一种方法更新了我的答案 - 在 this 情况下可能不是您需要的,但值得考虑
【解决方案3】:

首先,如果所有 NavigationComponents 都具有相同的名称,您将如何区分它们?如果您只想返回单个 NavigationComponent,则需要一个唯一标识符。

您可以使用 OfType() 方法返回一个类型的所有实例:

var navigationComponents = _components.OfType&lt;NavigationComponent&gt;();

从那里,您可以通过一些唯一标识符选择单个 NavigationComponent:

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

【讨论】:

  • 啊非常好,这似乎是我要找的。到目前为止,我认为每种类型只需要一个组件,但并非所有游戏对象都具有相同的组件集,一个可能有导航和传感器,一个可能只有一个传感器等。
  • 你的回答让我对我想要实现的目标有了更好的思考,我已经更新了这个问题。
猜你喜欢
  • 2018-05-11
  • 1970-01-01
  • 2012-10-04
  • 1970-01-01
  • 1970-01-01
  • 2019-07-30
  • 2013-08-30
  • 2022-10-01
  • 1970-01-01
相关资源
最近更新 更多