首页 > 解决方案 > 设置一个对象等于另一个对象C#时如何影响原始对象的属性

问题描述

制作一个文本游戏来练习编码并遇到一个非常奇怪的问题。我对此进行了搜索,但大多数人都试图将一个对象克隆到另一个对象,而我试图将对象 B 指向对象 A,因为我想更改同一个对象的属性。

public Armor Player_Chest_Armor { get; set; }
public Armor Player_Head_Armor { get; set; }
public Armor Player_Legs_Armor { get; set; }
    public Weapon Player_Weapon { get; set; }
    public Spell Player_Spell { get; set; }
    public List<Consumable> Consumables { get; set; }
    public List<IEquipment> Inventory { get; set; }

    [JsonConstructor]
public Player (string name) {
        this.Name = name;
        this.Consumables = new List<Consumable>();
        this.Inventory = new List<IEquipment>();
        this.Inventory.Add(new Weapon("bronze sword", 22, 28, 25, 1.2, false));
        this.Inventory.Add(new Armor("bronze chestplate", Armor.ArmorSlot.Chest, 35, 10, 20, false));
        this.Inventory.Add(new Armor("bronze helmet", Armor.ArmorSlot.Head, 12, 3, 7, false));
        this.Inventory.Add(new Armor("bronze legplates", Armor.ArmorSlot.Legs, 20, 5, 10, false));
        this.Consumables.Add(new Consumable("minor health potion", 3, Consumable.PotionType.Health, 50));
        this.Consumables.Add(new Consumable("minor mana potion", 3, Consumable.PotionType.Mana, 50));
        this.Player_Spell = new Spell("Fireball", 50, 0, 1);
    }

    public void EquipInitialGear() {
        this.EquipWeapon(this.Inventory[0] as Weapon);
        this.EquipArmor(this.Inventory[1] as Armor);
        this.EquipArmor(this.Inventory[2] as Armor);
        this.EquipArmor(this.Inventory[3] as Armor);
    }
    public void DecreaseArmorDurability() {
        try {
            this.Player_Chest_Armor.DecreaseDurability();
        }

这是我的播放器类中代码的相关部分。Player_ 无论是玩家装备的东西,无论是盔甲、武器等。我使用 IEquipment 接口将盔甲和武器对象连接到一个列表中,因为玩家库存应该包括所有东西。创建玩家时,EquipWeapon 和 EquipArmor 方法调用将这些库存索引对象设置为适当的玩家盔甲/武器槽。下面的代码,为了简单起见,假设没有武器已经装备,所以它直接进入 this.Player_Weapon = 武器行。

    public void EquipWeapon(Weapon weapon) {
        if (weapon.IsEquipped() == true) {
            Console.WriteLine("You have already equipped {0}.", weapon.GetName());
            return;
        }
        try {
            if (this.Player_Weapon.Equipped == true) {
                this.UnequipWeapon(this.Player_Weapon);
            }
        }
        catch(NullReferenceException) {}
        this.Player_Weapon = weapon;
        weapon.Equipped = true;
        Console.WriteLine("You have equipped {0}.", this.Player_Weapon.GetName());
    }

当我尝试更改 Player_Weapon 的属性值时,它会针对该对象发生更改,但库存中的对象(应该相同)具有原始属性值。降低耐久度在武器类中执行以下操作:

    public void DecreaseDurability() {
        this.Durability -= 1;
    }

每一轮攻击,我都会调用这个方法来将武器的耐久度降低 1 到 100。在调试模式下,我可以看到 this.Player_Weapon.Durability 从 100 到 99。但是,如果我去看看库存中应该是同一个对象,它的持久性是 100,这让我认为它正在创建对象的副本而不是引用原始对象。

我确信这是一个指针问题,但我只是看不出问题出在哪里。我希望对 Player_Weapon 属性的更改也影响对库存中该武器应指向的同一对象的更改。

为清楚起见进行了编辑:玩家、武器和盔甲都是类,而不是结构。

标签: c#pointersreference

解决方案


这是JSON保存游戏文件。这个错误是如此阴险,以至于我向 SO 寻求帮助的原因是它工作正常,但是当我退出在 JSON 中创建自动保存的游戏时,它会存储对象数据。但是,Player_Weapon 和任何 Player_Armor 都是指向库存中物品的类指针。只要程序正在运行,它就可以正常工作。创建 JSON 并保存数据后,Player 类实例似乎与库存分离。这是保存在文件中的内容:

"Player_Chest_Armor": {
    "Name": "bronze chestplate",
    "ArmorCategory": 1,
    "ItemValue": 35,
    "ArmorRating": 12,
    "Equipped": true,
    "Durability": 98
  },
  "Player_Head_Armor": {
    "Name": "bronze helmet",
    "ArmorCategory": 0,
    "ItemValue": 12,
    "ArmorRating": 6,
    "Equipped": true,
    "Durability": 98
  },
  "Player_Legs_Armor": {
    "Name": "bronze legplates",
    "ArmorCategory": 2,
    "ItemValue": 20,
    "ArmorRating": 8,
    "Equipped": true,
    "Durability": 98
  },
  "Player_Weapon": {
    "Name": "notched axe",
    "RegDamage": 16,
    "ItemValue": 10,
    "CritMultiplier": 1.2,
    "Equipped": true,
    "Durability": 98
  },
  "Player_Spell": {
    "Name": "Fireball",
    "ManaCost": 50,
    "Level": 1,
    "SpellCategory": 0,
    "FireOffense": {
      "BlastDamage": 30,
      "BurnDamage": 5,
      "BurnCurRounds": 3,
      "BurnMaxRounds": 3
    }
  },
  "Consumables": [
    {
      "Name": "minor health potion",
      "Quantity": 1,
      "ItemValue": 3,
      "Equipped": false,
      "PotionCategory": 0,
      "RestoreHealth": {
        "RestoreHealthAmt": 50
      }
    },
    {
      "Name": "minor mana potion",
      "Quantity": 1,
      "ItemValue": 3,
      "Equipped": false,
      "PotionCategory": 1,
      "RestoreMana": {
        "RestoreManaAmt": 50
      }
    }
  ],
  "Inventory": [
    {
      "$type": "DungeonGame.Weapon, DungeonGame",
      "Name": "bronze sword",
      "RegDamage": 27,
      "ItemValue": 25,
      "CritMultiplier": 1.2,
      "Equipped": false,
      "Durability": 98
    },
    {
      "$type": "DungeonGame.Armor, DungeonGame",
      "Name": "bronze chestplate",
      "ArmorCategory": 1,
      "ItemValue": 35,
      "ArmorRating": 12,
      "Equipped": true,
      "Durability": 98
    },
    {
      "$type": "DungeonGame.Armor, DungeonGame",
      "Name": "bronze helmet",
      "ArmorCategory": 0,
      "ItemValue": 12,
      "ArmorRating": 6,
      "Equipped": true,
      "Durability": 98
    },
    {
      "$type": "DungeonGame.Armor, DungeonGame",
      "Name": "bronze legplates",
      "ArmorCategory": 2,
      "ItemValue": 20,
      "ArmorRating": 8,
      "Equipped": true,
      "Durability": 98
    },
    {
      "$type": "DungeonGame.Weapon, DungeonGame",
      "Name": "notched axe",
      "RegDamage": 16,
      "ItemValue": 10,
      "CritMultiplier": 1.2,
      "Equipped": true,
      "Durability": 98
    }
  ]
}

正如保存游戏文件中的数据所示,没有对 Player Weapon/Armor 类数据的库存项目的引用,因此当游戏重新加载数据时,它会将这些对象与库存中的原始对象分离。Player Weapon/Armor 设置为这些值,但它不再引用库存中这些类的实例,因此它正在创建对象的副本。

保存的游戏代码正在通过序列化创建一个 JSON 文件。从https://www.newtonsoft.com/json/help/html/PreserveObjectReferences.htm,“默认情况下,Json.NET 将按值序列化它遇到的所有对象”。

保留引用的解决方案似乎是使用以下代码声明和初始化 json:

string json = JsonConvert.SerializeObject(people, Formatting.Indented,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });   

我之前没有使用 PreserveReferencesHandling,这导致了这个错误。


推荐阅读