10.6.2 编写类库
类和枚举都包含在一个类库项目 Ch10CardLib 中。这个项目将包含 4 个.cs 文件,Card.cs 包含Card类的定义,Deck.cs包含Deck类的定义,Suit.cs和Rank.cs文件包含枚举。
1. \Chapter10目录中创建一个新类库项目Ch10CardLib。从项目中删除Class1.cs。
2. 添加:Suit.cs和Rank.cs文件,如下所示修改:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Ch10CardLib { public enum Rank { Ace = 1, Deuce, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Ch10CardLib { public enum Suit { Club, Diamond, Heart, Spade, } }
3. 添加Card类,代码如下:
public class Card { public readonly Suit suit; public readonly Rank rank; public Card( Suit newSuit, Rank newRank ) { suit = newSuit; rank = newRank; } private Card() { } public override string ToString() { return "The " + rank + " of " + suit + "s"; } }
重写的ToString()方法将已存储的枚举值的字符串表示写入到返回的字符串中, 非默认的构造函数初始化suit和rank字段的值。
3. 添加Deck类
Deck类需要使用类图定义的如下成员:
Card[]类型的私有字段cards。
公共的默认构造函数。
公共方法GetCard(),它带有一个int参数cardNum,并返回一个Card类型的对象。
公共方法Shuffle(),它不带参数,返回void。
代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Ch10CardLib { public class Deck { private Card[] cards; public Deck() { cards = new Card[52]; for (int suitVal = 0; suitVal < 4; ++suitVal) { for (int rankVal = 1; rankVal < 14; ++rankVal) { cards[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal, (Rank)rankVal); } } } public Card GetCard(int cardNum) { if (cardNum >= 0 && cardNum <= 51) { return cards[cardNum]; } else { throw (new System.ArgumentOutOfRangeException("cardNum", cardNum, "Value must be between 0 and 51.")); } } public void Shuffle() { Card[] newDeck = new Card[52]; bool[] assigned = new bool[52]; Random sourceGen = new Random(); for (int i = 0; i < 52; ++i) { int destCard = 0; bool foundCard = false; while (false == foundCard) { destCard = sourceGen.Next(52); if (false == assigned[destCard]) { foundCard = true; } } assigned[destCard] = true; newDeck[destCard] = cards[i]; } newDeck.CopyTo(cards, 0); } } }
Shuffle()方法。这个方法创建一个临时的扑克牌数组,并把扑克牌从现有的cards数组随机复制到这个数组中。这个函数的主体是一个从 0~51 的循环,在每次循环时,都会使用.NET Framework中的System.Random类的实例生成一个0~51之间的随机数。进行了实例化后,这个类的对象使用方法Next(X)生成一个介于0~X之间的随机数。有了一个随机数后,就可以使用它作为临时数组中Card对象的索引,以便复制cards数组中的扑克牌。
为了记录已赋值的扑克牌,我们还有一个bool变量的数组,在复制每张牌时,把该数组中的值指定为 true。在生成随机数时,检查这个数组,看看是否已经把一张牌复制到临时数组中由随机数指定的位置上了,如果已经复制好了,就将生成另一个随机数。
这不是完成该任务的最高效的方式,因为生成的许多随机数都可能找不到空位置以复制扑克牌。但是,它仍能完成任务,而且很简单,因为C#代码的执行速度很快,我们几乎觉察不到延迟。
这个方法的最后一行使用System.Array类的CopyTo()方法(在创建数组时使用),把newDeck中的每张扑克牌复制回 cards 中。也就是说,我们使用同一个 cards 对象中的同一组 Card 对象,而不是创建新的实例。如果改用cards = newDeck,就会用另一个对象替代cards引用的对象实例。如果其他地方的代码仍保留对原cards实例的引用,就会出问题——不会洗牌。
至此,就完成了类库代码。