给CardLib添加深度复制

  下面把上述内容付诸于实践:使用ICloneable接口,复制Card、Cards和Deck对象,这在某些扑克牌游戏中是有用的,因为在这些游戏中不需要让两副扑克牌引用同一组Card对象,但肯定会使一副扑克牌中的牌序与另一副牌的牌序相同。

  在Ch11CardLib中,对Card类执行复制操作是很简单的,因为只需进行浅度复制(Card只包含值类型的数据,其形式为字段)。我们只需对类定义进行如下修改:

  1. public class Card : ICloneable
  2. {
  3. public object Clone()
  4. {
  5. return MemberwiseClone();
  6. }
  7. }

  ICloneable接口的这段实现代码只是一个浅度复制,无法确定在Clone()方法中执行了什么操作,所以这已经足以满足我们的需求。

  接着,需要对Cards集合类实现ICloneable接口。这个过程稍复杂些,因为涉及到复制源集合中的每个Card对象,所以需要进行深度复制:

  1. public class Cards : CollectionBase, ICloneable
  2. {
  3. public object Clone()
  4. {
  5. Cards newCards = new Cards();
  6. foreach(Card sourceCard in List)
  7. {
  8. newCards.Add((Card)sourceCard.Clone());
  9. }
  10. return newCards;
  11. }
  12. }

  最后,需要在Deck类上实现ICloneable接口。这里存在一个小问题:因为Ch11CardLib中的Deck类无法修改它包含的扑克牌,所以没有洗牌。例如,无法修改有给定牌序的Deck实例。为解决这个问题,为Deck类定义一个新的私有构造函数,在实例化Deck对象时,可以给该函数传送指定的Cards集合。所以,在这个类中执行复制的代码如下所示:

  1. public class Deck : ICloneable
  2. {
  3. public object Clone()
  4. {
  5. Deck newDeck = new Deck(cards.Clone() as Cards);
  6. return newDeck;
  7. }
  8. private Deck(Cards newCards)
  9. {
  10. cards = newCards;
  11. }
  12. }

  再次用一些简单的客户代码进行测试。与以前一样,这应放在客户项目的Main()方法中,以便进行测试(包含在本章下载代码的Ch11CardClient\Program.cs文件中):

  1. Deck deck1 = new Deck();
  2. Deck deck2 = (Deck)deck1.Clone();
  3. Console.WriteLine("The first card in the original deck is : {0}",
  4. deck1.GetCard(0));
  5. Console.WriteLine("The first card in the cloned deck is : {0}",
  6. deck2.GetCard(0));
  7. deck1.Shuffle();
  8. Console.WriteLine("Original deck shuffled.");
  9. Console.WriteLine("The first card in the original deck is : {0}",
  10. deck1.GetCard(0));
  11. Console.WriteLine("The first card in the cloned deck is : {0}",
  12. deck2.GetCard(0));
  13. Console.ReadKey();