将已实例化的对象作为新对象传递

本文关键字:对象 新对象 实例化 | 更新日期: 2023-09-27 18:19:55

这个问题听起来可能很奇怪,也请原谅我,因为我的程序流程可能都错了,但非常感谢对我的情况提供任何帮助!

我有一个名为EnemyFighter的类,它包含了所有信息,如生命值、攻击强度等。当程序启动时,它会将不同类型的敌人添加到列表中,比如:

// Build enemies
Enemies = new Dictionary<int, EnemyFighter>();
Enemies.Add(1, new EnemyFighter());
Enemies[1].Name = "Beaver";
Enemies[1].SetLevel(1);
Enemies.Add(2, new EnemyFighter());
Enemies[2].Name = "Unicorn";
Enemies[2].SetLevel(3);

然后我有敌人团体,所以你可以与多个敌人作战,也许两个海狸说:

// Enemy groups
EnemyGroups = new List<EnemyFighter[]>();
EnemyGroups.Add(new EnemyFighter[] { Enemies[1] }); // Beaver
EnemyGroups.Add(new EnemyFighter[] { Enemies[1], Enemies[1] }); // Beaver x2
EnemyGroups.Add(new EnemyFighter[] { Enemies[2] }); // Unicorn

但当我去运行应用程序时,在攻击其中一个敌人时(当攻击两只海狸时),我意识到我在攻击"两个",因为本质上两个"海狸"都是同一个对象。

有人能给我一个更好的方法来设置这种情况吗?在这种情况下,我有一个对象的模板,但想把它的唯一版本传递到列表中。类似这样的东西:

EnemyGroups.Add(new EnemyFighter[] { new Enemies[1], new Enemies[1] });

再次感谢任何帮助,提前感谢!

将已实例化的对象作为新对象传递

"复制"对象不是一件小事,在大多数情况下也是不可取的。

在这种情况下,最简洁的方法可能是这样做:

public class EnemyPrototype
{
    public string Name  { get; set; }
    public int Level { get; set; }
}
public class EnemyProvider
{
    private Dictionary<string, EnemyPrototype> _prototypes = 
        new Dictionary<string, EnemyPrototype>();
    public void AddPrototype(string key, EnemyPrototype prototype)
    {
        _prototypes[key] = prototype;
    }
    public EnemyFighter GetEnemy(string key)
    {
        var prototype = _prototypes[key];
        var fighter = new EnemyFighter();
        fighter.Name = prototype.Name;
        fighter.SetLevel(prototype.Level);
        return fighter;
    }
}

现在,您可以创建这样的新敌人原型(假设有一个名为_provider的私有EnemyProvider实例成员-根据您的结构进行调整):

_provider = new EnemyProvider();
_provider.AddPrototype("Beaver", new EnemyPrototype {Name = "Beaver", Level = 1});

然后买一个这样的新:

var enemy = _provider.GetEnemy("Beaver");

所以加上你的两只海狸就变成了:

EnemyGroups.Add(new EnemyFighter[] { _provider.GetEnemy("Beaver"),
                                     _provider.GetEnemy("Beaver") });

你也可以在EnemyProvider类中添加一个方法,让你传递一个int并返回一个敌人数组!

因此,正如您所说,攻击两者的原因是它们本质上是同一个对象。为了解决这个问题,你应该复制这个对象。你可以创建这样一个方法:

private EnemyFighter DuplicateEnemy(EnemyFighter original){
    var newEnemy = new EnemyFighter(){
        Name = original.Name
    };
    newEnemy.setLevel(original.Level);
    return newEnemy;
}
EnemyGroups.Add(new EnemyFighter[] { DuplicateEnemy(Enemies[1]) }); // Beaver
EnemyGroups.Add(new EnemyFighter[] { DuplicateEnemy(Enemies[1]), DuplicateEnemy(Enemies[1]) }); // Beaver x2
EnemyGroups.Add(new EnemyFighter[] { DuplicateEnemy(Enemies[2]) }); // Unicorn

这将返回一个具有相同值的新对象,您可以将其添加到列表中。

你也可以看看AutoMapper,它是一个用于复制对象的库,非常灵活,我以前在项目中使用过它,发现它非常有用。http://automapper.org/

在将"template"对象添加到字典时,可以使用MemberwiseClone()方法创建该对象的浅层副本。

https://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspX

或者,您可以实现一种方法来对对象进行深度复制。创建浅拷贝和深度拷贝的示例在上面的MemberwiseClone()链接中给出。

正如您所注意到的,您在数组中多次引用同一对象。最重要的是,您需要放入不同的新对象,而不是相同的引用。

我建议的方法是制造某种工厂,创造并回报一个新的敌人。这样做的好处是可以从普通的敌人库中创建敌人,而不必担心在克隆之前所做的任何修改。

public static class EnemyFactory
{
    public static EnemyFighter MakeBeaver()
    {
        var beaver = new EnemyFighter
        {
            Name = "Beaver",
            Level = 1
        };
        return beaver;
    }
}

然后使用工厂创建实例:

EnemyGroups.Add(new EnemyFighter[] { EnemyFactory.MakeBeaver(), EnemyFactory.MakeBeaver() }); // Beaver x2

我认为您需要"克隆"您的初始对象。请查看MSDN中的ICloneable接口文档和示例:https://msdn.microsoft.com/en-us/library/system.icloneable.clone(v=vs.100).aspx