在教程中需要帮助理解此代码

时间:2012-01-11 20:30:06

标签: c# asp.net

此代码随机抽取52张卡片,并在每次点击时将4张文件放入4盒文本中。我对它的工作原理有了一般的了解,但我需要帮助清除一些混乱或理解它是如何工作的。我对那些我不太确定的人发表了大胆的评论。

namespace Cards
{
    enum Value { Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King, Ace}
}

namespace Cards
{
    enum Suit { Clubs, Diamonds, Hearts, Spades }
}

namespace Cards
{
    class PlayingCard 
    {
        private readonly Suit suit;
        private readonly Value value;

        public PlayingCard(Suit s, Value v)
        {
            this.suit = s;
            this.value = v;
        }

        public override string ToString()
        {
            string result = string.Format("{0} of {1}", this.value, this.suit);
            return result;
        }

        public Suit CardSuit() // **Not sure why this is here.  Maybe for future use?**
        {
            return this.suit;
        }

        public Value CardValue() // **Same as above**
        {
            return this.value;
        }
    }
}

namespace Cards
{
using System;
using System.Collections;

class Pack // **Data Access Layer?**
{
    public const int NumSuits = 4; 
    public const int CardsPerSuit = 13;
    private PlayingCard[,] cardPack;
    private Random randomCardSelector = new Random();

    public Pack() //**Storing all elements**
    {
        this.cardPack = new PlayingCard[NumSuits, CardsPerSuit]; // 

        for (Suit suit = Suit.Clubs; suit <= Suit.Spades; suit++) // 
        {
            for (Value value = Value.Two; value <= Value.Ace; value++)
            {
                this.cardPack[(int)suit, (int)value] = new PlayingCard(suit, value);
            }
        }
    }

    public PlayingCard DealCardFromPack() // **Purpose: To dealing unique cards**
    {
        Suit suit = (Suit)randomCardSelector.Next(NumSuits); // **picks random 0-3 from Suit**
        while (this.IsSuitEmpty(suit)) // **Purpose: Checks if empty but don't know how it works**
        {
            suit = (Suit)randomCardSelector.Next(NumSuits);
        }
        Value value = (Value)randomCardSelector.Next(CardsPerSuit);
        while (this.IsCardAlreadyDealt(suit, value)) // ?
        {
            value = (Value)randomCardSelector.Next(CardsPerSuit);
        }
        PlayingCard card = this.cardPack[(int)suit, (int)value];
        this.cardPack[(int)suit, (int)value] = null; // **sets the current element to null so it isn't reused.**
        return card;
    }

    private bool IsSuitEmpty(Suit suit) // **checks if empty**
    {
        bool result = true;

        for (Value value = Value.Two; value <= Value.Ace; value++) //**Checks Null or not**
        {
            if(!IsCardAlreadyDealt(suit, value))
            {
                result = false;
                break;
            }
        }
        return result;
    }

    private bool IsCardAlreadyDealt(Suit suit, Value value) //**returns current element null?**
    {
        return (this.cardPack[(int)suit, (int)value] == null);
    }
}
}

namespace Cards
{
using System;
using System.Collections;

class Hand // **Business layer?**
{
    public const int HandSize = 13;
    private PlayingCard[] cards = new PlayingCard[HandSize]; // **single array?  Thought it had to be 2 because of 2 parameters?**
    private int playingCardCount = 0;

    public void AddCardToHand(PlayingCard cardDealt)//**Confusion when class used as variable**
    {
        if (this.playingCardCount >= HandSize)
        {
            throw new ArgumentException("Too many cards");
        }
        this.cards[this.playingCardCount] = cardDealt; //**Confused...cardDealt value going in card[]?  How does that work..**
        this.playingCardCount++;//**incrementing, how is this helpful**
    }

    public override string ToString()  //to show all 13 cards in hand
    {
        string result = "";
        foreach (PlayingCard card in this.cards)
        {
            result += card.ToString() + "\n";
        }

        return result;
    }
}
}

namespace Cards
{
    public partial class Game : Window
    {
        public const int NumHands = 4;

        private Pack pack = null;
        private Hand[] hands = {new Hand(), new Hand(), new Hand(), new Hand()}; //**Creates 4 different list all single array?**

        public Game()
        {
            InitializeComponent();
        }

        private void dealClick(object sender, RoutedEventArgs e)
        {
            try
            {
                pack = new Pack(); 

                for (int handNum = 0; handNum < NumHands; handNum++) //
                {
                    hands[handNum] = new Hand();
                    for (int numCards = 0; numCards < Hand.HandSize; numCards++)
                    {
                        PlayingCard cardDealt = pack.DealCardFromPack(); //**Deals 13 random cards into each array.  Don't understand how it worked though.**
                        hands[handNum].AddCardToHand(cardDealt);
                    }
                }

                north.Text = hands[0].ToString();
                south.Text = hands[1].ToString();
                east.Text = hands[2].ToString();
                west.Text = hands[3].ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    }
}

6 个答案:

答案 0 :(得分:2)

部分答案:

卡,包和手是数据访问层的一部分。它们是应用程序的模型(对象/实体/域模型)。

如果你有代码将这些代码保存/加载到Db或文件,那么该代码就是DAL。

 private readonly Suit suit; // **readonly to protect from changing value?**
 private readonly Value value;

是的,只读,因为一旦卡存在,它永远不会改变。这会破坏游戏的逻辑(和诚实)。将这个概念融入模型是一个好主意。

    public PlayingCard(Suit s, Value v) //**Returning value of every card**

这不会返回任何内容,它是PlayingCard类的构造函数

    public Suit CardSuit() // **Not sure why this is here.  Maybe for future use?**

不,不是'未来使用'。这是课堂中非常重要的一部分,让其他代码找出它是哪一张卡片。请注意,只读字段suit是私有的。

答案 1 :(得分:2)

<强>游戏牌

class PlayingCard // **Data Access Layer?**

这看起来像商业模式,与数据访问无关。事实上,我根本没有看到代码中使用的任何数据持久性(如数据库),因此该应用程序没有数据访问层。

private readonly Suit suit; // **readonly to protect from changing value?**

正确。这使得价值不可变&#34;因为一旦构造了对象就无法改变它。请注意,此值在类(&中)中设置。构造函数。因此,要有一个不同的suit,需要构建一个不同的类实例(不同的对象,就像它一样)。

public PlayingCard(Suit s, Value v) //**Returning value of every card**

不是真的。这是该类的构造函数。每次创建类的新实例(例如,var card = new PlayingCard(someSuit, someValue))时,此方法都会执行以构造该实例。

public Suit CardSuit() // **Not sure why this is here.  Maybe for future use?**

这是获取该卡的当前Suit值的方法。所以使用卡片的东西可以看出它适合它。请注意,它返回的值为private,因此无法从课外读取。 (我不同意这个实现,更喜欢属性而不是方法,但完全是另一个主题。)

<强>包

class Pack // **Data Access Layer?**

不,和以前一样。这些都是模特。我稍后会谈到这一点。

public Pack() //**Storing all elements**

构造函数,与以前一样。

public PlayingCard DealCardFromPack() // **Purpose: To dealing unique cards**

是。这是对Pack对象的逻辑方法。可以合理地期望Pack可用于处理PlayingCard。也就是说,可以从Pack中提取它们。这就是这个方法的用途。

Suit suit = (Suit)randomCardSelector.Next(NumSuits); // **picks random 0-3 from Suit**

是的,从给定的已知Suit枚举中选择一个随机Suit

while (this.IsSuitEmpty(suit)) // **Purpose: Checks if empty but don't know how it works**

嗯,一包没有无数的牌。每次删除一个,它都会从持有卡片的班级(Pack)中删除。因此,它不再适用于不再拥有卡片的套装。因此,这会在尝试处理之前检查是否仍有卡片留下。

this.cardPack[(int)suit, (int)value] = null; // **sets the current element to null so it isn't reused.**

是的,这与上一部分有关。发卡时,它已被删除。所以这个&#34;删除&#34;来自Pack

private bool IsSuitEmpty(Suit suit) // **checks if empty**

这是类中的一种辅助方法。处理卡片的方法需要检查套装是否为空,因此编写此方法进行检查。 (而不是将所有代码都放在一个大方法中,这将是糟糕的设计。)

private bool IsCardAlreadyDealt(Suit suit, Value value) //**returns current element null?**

这是另一种辅助方法。交易卡时,人们无法处理已经处理过的卡。所以这会检查。所以基本上,使用这些方法的更大方法是:&#34;在随机看西装时,我需要一套仍然有卡片的西装。在随意查看该套装中的牌时,我需要一张仍然在牌组中的牌。现在从卡组中取出该卡。&#34;

<强>手

class Hand // **Business layer?**

有点儿。有点。这是另一种模式。我会在一秒钟内完成。

private PlayingCard[] cards = new PlayingCard[HandSize]; // **single array?  Thought it had to be 2 because of 2 parameters?**

2个参数是什么?它是一系列卡片。每个Hand基本上都是一组卡片。这会存储对这些卡的引用。

public void AddCardToHand(PlayingCard cardDealt)//**Confusion when class used as variable**

我不确定你在评论中提出的问题。这种方法的目的是在手上添加一张牌。因此,对卡的引用将传递给该方法,并且该方法将其添加到当前手中。

this.cards[this.playingCardCount] = cardDealt; //**Confused...cardDealt value going in card[]?  How does that work..**

在这种情况下,cardDealt是要添加到Hand的卡片。它与&#34;处理&#34;无关。上一课的概念。它只是变量的名称。这条线正在做的是将该卡添加到Hand

中的卡片组中
this.playingCardCount++;//**incrementing, how is this helpful**

现在一手牌更大。因此,如果您将第三张牌添加到手牌中,则会将其添加为this.cards[2]。在此之后添加另一张牌需要进入this.cards[3],否则它将取代现有的牌,这并不是所需的效果。

public override string ToString()  //to show all 13 cards in hand

正确,这种方法只是打印出目前Hand中的内容。


<强>结论

好的,既然我们已经完成了这个练习(并且可以随意要求澄清),那就让我们谈谈一下&#34;模型&#34;&#34;是。它是一种商业层,因为它是一个商业对象。&#34;也就是说,它是一个封装商业概念的对象。

在本申请案例中,有三个具体概念代表&#34;业务&#34;把牌交给一只手。这些是:

  • 一张卡
  • 甲板,包含预先确定的有限卡片列表
  • 一只手,其中包含一张给予它的卡片列表

每个模型都有责任在内部维护其业务逻辑。这是通过其内部变量,属性,方法等来完成的。像DealCardFromDeck这样的事实是关于业务如何工作的现实概念,这些概念被编码到模型中。其他模型并不关心该模型如何实现该业务逻辑,他们只知道可以从一个卡片处理一张卡片。 (这是该模型与其他模型的外部可见界面的一部分。)

业务逻辑(业务层,如果你愿意的话,虽然这段代码没有真正拥有&#34;层&#34;本身)可以看出模型如何相互作用。最简单的是,我上面三个模型的列表基本上描述了它们如何相互作用。代码只是该逻辑的一种实现。

答案 2 :(得分:0)

问题:// 数据访问层?

答案:不可以。这是一个模拟单张卡的课程


问题:// 业务层?

答案:不可以。这是一个为卡片集合建模的课程。

答案 3 :(得分:0)

代码是自我解释的。我想你需要深入了解如何编程,例如:

  • 只读变量是以这种方式使用的,因为它们的值只能在类的构造函数上设置。
  • 你说它返回每张卡的值的方法实际上是构造函数,它用于指定卡的套装和值。
  • CardSuit()和CardValue()方法返回卡片的当前套装和值。

答案 4 :(得分:0)

大多数东西都是基本的C#,一个体面的程序员应该马上认出来。处理随机卡的算法存在缺陷。这是大纲:

选择随机套装。

如果已经处理了该套装的所有牌,请转到步骤1.

选择随机排名。

如果已经处理了所选套装和等级的卡片,请转到步骤3.

从卡座中取出选定的卡片。

处理它。

这个例子最好地说明了这个有缺陷的原因。下面的牌留在牌组中(2C = 2个俱乐部):

2C 2S 3S 4S

在现实生活中,交易2个球杆的几率是1/4。使用这个算法,它是1/2,因为它有同等的机会选择交易俱乐部,因为它做了一个铲子。可以把它想象成将甲板分成4堆并随机选择一堆来处理。每一堆代表一套西装。这不是这样做的方法。

我允许你瞧不起这段代码的作者。哦,找到一本更好的书。

答案 5 :(得分:-1)

    public Suit CardSuit() // **Not sure why this is here.  Maybe for future use?**
    {
        return this.suit;
    }

    public Value CardValue() // **Same as above**
    {
        return this.value;
    }

这些方法通常被称为“getters”。调用时,这些方法返回类的内部状态(变量)。程序员通常会将它们添加到呼叫中,即使没有立即使用它们。

老实说,我认为一本关于C#的入门书比Stack Overflow更能为你服务。