【问题标题】:Using an interface as type parameter to generic method使用接口作为泛型方法的类型参数
【发布时间】:2012-11-28 20:35:46
【问题描述】:

我正在使用 C# 开发 Unity3d 游戏,在很多情况下,我们需要访问 GameObject 的特定成员(例如,int health)。我们使用如下代码来做到这一点:

GameObject obj;
if(obj.GetComponent<Player>() != null) {
    obj.GetComponent<Player>().health--;
}
else if(obj.GetComponent<Robot>() != null) {
    obj.GetComponent<Robot>().health--;
}
// more painful code

我想做的是让所有这样的类实现像IHealth 这样的接口,然后执行obj.GetComponent&lt;IHealth&gt;().health--;。不过,这可能吗?我环顾四周,似乎无法将接口用作类型参数。

【问题讨论】:

  • 效果很好。你试过了吗?
  • @SLaks 不,接口只能有方法或属性,不能有字段。
  • 您可能需要考虑捕获您在 if 块中使用的结果,这样您就不会为每个对象类型调用两次 GetComponent。
  • @Servy:是的;这些应该是属性。
  • 如果 GameObject 是一个结构,而不是一个类,那么当你将它装箱到接口时,你会得到一个副本并改变副本,而不是实际的对象,所以它不会做你想做的事想。最重要的是,接口只能有方法或属性,不能有字段。

标签: c# generics unity3d


【解决方案1】:

事实证明,这并不容易,而且可能不是一个好主意。问题是GetComponent&lt;T&gt; 期望类型参数T 派生自Component(当然,Unity 没有记录这一点)。我需要使RobotPlayer 等都派生自一个抽象类,该抽象类本身继承自Component(通过MonoBehaviour),并包含有关健康和其他领域的信息。我怀疑这是否值得;它可能会在其他地方导致同样多的混乱代码。

【讨论】:

    【解决方案2】:

    为什么不只拥有一个组件Health。然后将其附加到您的游戏对象,在任何需要健康的脚本上添加 RequiresComponent 注释,然后通过 GetComponent&lt;Health&gt;(); 获取它(将其缓存在私有变量中的同一对象脚本中)

    如果你有多个总是在一起的“统计数据”,你可以让它更容易

    public class Status : MonoBehaviour
    {
        public float health;
        public float armor;
    }
    

    我发现,这是一个令人惊讶的普遍“问题”。不确定这是否是一个问题,因为这种想法确实可以帮助解决一些问题......

    【讨论】:

    • 听起来不错;我不知道RequiresComponent。我将在以后的项目中使用它。
    【解决方案3】:

    我认为您有一个基本的设计问题,因为您的消费代码需要了解每种类型。如果无论类型如何都采取相同的操作,那么只需使用良好的 ol' 多态性:

    public class Player: IHealth {}
    
    public class Robot: IHealth {}
    

    不要使用一堆if 语句,而是从 GetComponent 中删除通用参数并让它返回一个 IHealth 对象:

    public IHealth GetComponent()
    {
        // return IHealth object here
    }
    

    在您的调用代码中:

    obj.GetComponent().DecreaseHealth();
    

    【讨论】:

      【解决方案4】:

      正如 SLaks 所指出的,接口作为泛型类型参数是完全可以接受的。如果您的问题是通过接口访问字段,那么我建议只需将您的字段更改为属性。如果由于某种原因您绝对必须保留该字段,只需提供一个隐式实现接口的包装属性。如果问题是字段和属性的名称完全相同(包括大小写),则可以显式实现接口:

      public interface IHealth { int Health { get; set; } }
      
      // Change to a property
      public class Ogre : IHealth { public int Health { get; set; } }
      
      // Casing is different, so interface can be implemented implicitly
      public class Player : IHealth {
          int health;
          public int Health { get { return health; } set { health = value; } }
      }
      
      // Name collision with Pascal casing, so implement explicitly
      public class Robot : IHealth {
          int Health;
          int IHealth.Health { get { return Health; } set { Health = value; } }
      }
      
      // Later
      GameObject obj;
      var o = obj.GetComponent<IHealth>();
      if (o != null) o.Health--;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多