【问题标题】:Tricky Inheritance-Related Architecture棘手的继承相关架构
【发布时间】:2020-07-16 22:00:22
【问题描述】:

我正在尝试用 C# 为视频游戏中的不同角色类型设计一个系统。我的字符类型有一个简单的继承树,它有一个Humanoid 基类,它有几个派生类,例如HumanAlien 等。这些字符类型中的每一个都包含一个带有几个部分的主体。目前这些部分存储在Body 类的实例中,每个部分都由BodyPart 类的实例描述。

当我想为BodyPart 派生类时,主要的复杂性就出现了,因为每个字符类型的身体部位都会有一些额外的特定属性。我希望将BodyPart 实例分组到Body 类中的主要原因是我可以使用索引器,因为我需要能够迭代它们并且仍然能够使用点符号轻松访问特定部分。

以下伪代码应该让您了解我正在尝试实现的目标以及我已经尝试如何实现它。

public class BodyPart
{
    public float health;
    //...
}

public class HumanBodyPart : BodyPart
{
    public float exampleHumanStuff;
    //.. human specific stuff
}

public class AlienBodyPart : BodyPart
{
    public float exampleAlienStuff;
    //.. alien specific stuff
}

public class Humanoid
{
    public class Body<T> where T : BodyPart
    {
        public T head;
        public T leftArm;
        public T rightArm;
        //...

        public T this[int i]
        {
            get
            {
                switch (i)
                {
                    case 0:
                        return head;
                    case 1:
                        return leftArm;
                        //...
                }
            }
        }
    }

    public virtual Body<BodyPart> body { get; }

    public void Example {
        body.head.health = 50;
        body[2].health = 55;
    }
}

public class Human : Humanoid
{
    public Body<HumanBodyPart> humanBody;
    public override Body<BodyPart> body { get { return (Body<BodyPart>)humanBody; } }

    public void Example
    {
        body.rightArm.exampleHumanStuff = 5;
    }
}

public class Alien : Humanoid
{
    public Body<AlienBodyPart> alienBody;
    public override Body<BodyPart> body { get { return (Body<BodyPart>)alienBody; } }

    public void Example
    {
        body.leftArm.exampleAlienStuff = 5;
    }
}

这种方法的死胡同是Body 类不是逆变的(我认为?)所以将Body&lt;HumanBodyPart&gt; 转换为Body&lt;BodyPart&gt; 将不起作用。但是我想不出另一种方法来从基类Humanoid 访问Human 中的Body&lt;HumanBodyPart&gt; 类实例,因此在基类中它被视为Body&lt;BodyPart&gt;。有更好的方法吗?

【问题讨论】:

  • 太宽泛了。在这里可以采取多种方法,其中许多与您现在所拥有的完全不同。但请记住,如果泛型类型变化要起作用,您需要将相关类型声明为 接口 而不是类,因为类型变化仅支持接口而不是具体类型。

标签: c# class generics inheritance indexer


【解决方案1】:

您可以将Humanoid 设为BodyPart 本身的泛型:

public class Humanoid<T> where T : BodyPart
{
    public class Body 
    {
        public T head;
        //...

        public T this[int i]
        {
            get
            {
                switch (i)
                {
                    case 0:
                        return head;
                        //...
                }
                return default;
            }
        }
    }

    public Body body { get; }

    public void Example()
    {
        body.head.health = 50;
        body [2].health = 55;
    }
}

public class Human : Humanoid<HumanBodyPart>
{
    public void Example()
    {
        body.rightArm.exampleHumanStuff = 5;
    }
}

public class Alien : Humanoid<AlienBodyPart>
{
    public void Example()
    {
        body.leftArm.exampleAlienStuff = 5;
    }
}

此外,此类层次结构不支持所有可能的用例(例如,不同 Humanoid 的集合,但您可以使用非通用接口解决一些问题(对于 HumanoidBody)。

至于方差,它仅支持 C# 中的 interfaces and delegates(和数组,但不要使用它)。在你的情况下,这样的事情会起作用:

public interface IBody<out TInner>
{
    public TInner head { get; }
    public TInner leftArm { get; }
    public TInner this[int i] { get; }
}

IBody<HumanBodyPart> humanBody = ...;
IBody<BodyPart> body = humanBody; 

【讨论】:

    【解决方案2】:

    我假设您想要分配 Humanoid 类的特定 BodyPart。我会用这个:

    public class Humanoid <T> where T : BodyPart
    {
        public class Body<T>
        {....
    
    

    最后,当您描述人类时,您可以使用:

    public class Human : Humanoid <HumanBodyPart>
    {.......
    

    【讨论】:

      猜你喜欢
      • 2018-07-07
      • 2011-01-30
      • 2014-08-02
      • 1970-01-01
      • 1970-01-01
      • 2011-04-11
      • 1970-01-01
      • 1970-01-01
      • 2021-02-07
      相关资源
      最近更新 更多