【问题标题】:C# polymorphism with cloning带有克隆的 C# 多态性
【发布时间】:2011-01-12 05:08:11
【问题描述】:

让我先解释一下我想要实现的目标。

我正在制作一款玩家可以拾取道具的游戏(出于某种原因,我在游戏中将其称为“战利品”)。所以这是我的 Loot 类的一个非常基本的形式:

public class Loot : Sprite
{
    //Properties and fields...

    public Loot(/*parameters...*/)
    {
    }

    public virtual OnPickUp(Player player)
    {
    }
}

PlayerSprite 是我的课程。这是我的问题:我的 sprite 类拥有一个位置(一个 Vector2,一个来自 XNA 的结构,表示一个向量),根据我对 C# 工作原理的理解,复制两个 sprite 将复制彼此的 reference,因此,每当我改变一个人的位置时,它也会改变另一个人的位置。

每当产生战利品时,我都有一个类持有可能产生的战利品,它会返回一个随机战利品,战利品会被复制并显示到屏幕上,但只要我得到多个相同类型的战利品loot,由于参考副本问题,它搞砸了(两者将共享相同的位置,只有一个可见)。

因此,每当我需要生成新的战利品时,我都会尝试创建一个新的 Loot 对象,因此我必须提供一个带有多个参数的构造函数,以便以这种方式轻松复制战利品: (让我们假设 toSpawn 可以是从 Loot 类派生的任何类型,例如 HealthPack 或其他)

Loot spawning = new Loot(toSpawn.Position, toSpawn.Color /*etc...*/);

在我决定实施“OnPickUp”之前,它看起来不错。由于我创建了一个新的 Loot 对象而不是正确的子类,所以 toSpawnOnPickUp 函数的功能会消失并拾取战利品最终什么都不做(它只会调用基类 Loot 的空函数)。

显然,我在这里做错了什么。我没有太多的编程经验,更不用说 C#,所以我真的不知道如何解决这个问题。我尝试使用表示 OnPickUp 功能的 Action 对象,它可以工作但仍然限制了我很多(因为我必须将静态函数作为 Action 传递,因此限制我使用播放器的信息参数并阻止我使用子类特定信息)。

所以,最后,我的问题真的是:什么是更好的设计来允许具有重载函数的子类,但仍然能够克隆基类及其属性? p>

感谢您宝贵的时间。

如果有什么不够清楚(我知道还不够清楚),请在 cmets 中问我,我会尝试更详细地描述我的问题。

【问题讨论】:

  • 听起来您只想在基类中创建一个CloneCopy 虚函数,并让子类实现自己的复制功能。我错过了什么吗?
  • 基本上是的,但是我需要为我的 Loot 类的每个子类实现一个“复制”功能吗?对于 HealthPack、WeaponSpeedIncrease 等?
  • 您的基类可以只调用MemberwiseClone,而每个子类只需复制无法复制的成员(例如,它们必须包含对不同Location对象的引用)。
  • @Gabe 你为我省去了很多打字和代码重复... O_O 谢谢,不知道那个美妙的功能。

标签: c# xna polymorphism clone


【解决方案1】:

让你的 Loot 类实现 ICloneable

public abstract class Loot : ICloneable
{
    public virtual object Clone()
    {
        Type type = this.GetType();
        Loot newLoot = (Loot) Activator.CreateInstance(type);
        //do copying here
        return newLoot;
    }
}

【讨论】:

  • 这就是我所做的,但是我的游戏有一个可能的战利品列表,那么我怎么知道我应该将克隆对象转换为什么类型? (Loot)toSpawn.Clone() 让我回到了 OnPickUp 功能的丧失(通过投射到战利品)。
  • 有效!非常感谢! :) (但我仍然怀念 C++ 的复制构造函数 =( )
  • @ivo 我同意你的第一点。对于 b 我将假设 OP 可以阅读 msdn 文档并希望找出它可能失败的原因。对于 c,我不会对此感到失望。此代码旨在进入 Loot 基类,并创建继承它的任何类的实例。然后该实例变得沮丧,因为 OP 说他只需要复制 Loot 的属性。
  • @Yads:操作是的,误读了您的解决方案。逆转。除非编辑答案,否则它不会让我反转。请编辑它,我会投票给 3。
【解决方案2】:

我想我一定错过了这个问题,因为Vector2 是一个结构而不是一个类。将一个 Vector2 直接分配给另一个会自动创建一个副本。

var v2a = new Vector2(10.0f, 20.0f);
var v2b = v2a; //`v2b` is a new instance of `Vector2` distinct from `v2a`.

您的问题听起来像是 PossibleLoots 类没有随机化每个创建的 Loot 实例的位置。

我假设您的游戏逻辑说“每个级别最多应产生 10 个战利品”,因此您的 PossibleLoots 类会在启动时创建所有可能的 Loot 对象,但不会显示一个,除非它被要求“产生战利品”。

那么,如果是这样的话,这不正是你需要的吗?

public class PossibleLoots
{
    private IList<Loot> _loots = new List<Loot>();

    public PossibleLoots(int maxLoots)
    {
        for (var i = 0; i < maxLoots; i++)
        {
            _loots.Add(new Loot(this.GetRandomPosition()));
        }
    }

    private Vector2 GetRandomPosition()
    {
        // Your logic here to create suitable locations for loot to appear
    }

    // Rest of your `PossibleLoots` code
    // to spawn each `Loot` and to deplete the `_loots`
    // collection when the player picks up each `Loot`
}

如果我错过了这个问题的重点,请告诉我。

【讨论】:

  • 我对 Vector2 的假设不利,这是我的一个愚蠢的错误,感谢您指出这一点。 :) 至于我的游戏,它是一个射手。我不知道如何存储不同的武器和战利品,所以我决定在发布时将它们全部加载,并在需要时创建一个副本,但我最终遇到了子类问题,因为我没有正确克隆(我正在投射到一个战利品对象)。不过,Yads 的提议似乎可行。 :) 但也感谢您的帮助! ;)
【解决方案3】:

Vector2struct 而不是class,所以你原来的问题听起来像是由于持有对同一个 Loot 对象的两个引用,而不是有两个不同的 Loot 对象引用相同的位置。

负责创建 Loot 对象的类应该创建相关 Loot 子类的新实例,而不是创建基类。在您的设计中,Loot 可能应该是一个抽象类甚至是一个接口,您应该使用abstract factory pattern 来创建所需类型的 Loot 对象。

例如:

class LootFactory
{
    const int numLootTypes = 3;

    public Loot CreateLoot(Vector2 position)
    {
        Random rand = new Random();
        int lootIndex = rand.Next(numLootTypes);
        if (lootIndex == 0)
        {
            return new HealthPack(position);
        }
        if (lootIndex == 1)
        {
            ...
        }
        ...
    }
}

【讨论】:

    【解决方案4】:

    对我来说,听起来你仍然在以错误的方式解决这个问题。虽然给出的答案可行,但这不是你应该做的事情。

    如果您想生成一个新的战利品,并且希望它是可能的战利品类型中的随机,您的 Loot 类的构造函数应该完成所有这些工作。应该不需要克隆或 ICloneable。

    当一个新的战利品对象被创建时,它应该随机成为它的战利品类型,无需在某处保留所有可能类型的列表并从中克隆。然后,新的战利品对象将拥有自己的位置和行为,并完全按照您的预期工作。

    还是我只是误解了您要完成的工作?

    【讨论】:

      猜你喜欢
      • 2015-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-28
      • 1970-01-01
      • 2012-06-28
      • 2011-08-06
      • 1970-01-01
      相关资源
      最近更新 更多