将项目从一个阵列移动到另一个阵列的优雅技术

时间:2009-03-15 22:28:17

标签: c# arrays pass-by-reference

背景:纸牌游戏;我想以一种干净的方式将卡牌从牌组中交给每个玩家。

这就是我的想法:

public static CardGame.IGame DealAll(this CardGame.IGame objThis, CardGame.Card[] cards)
    {
        if (objThis.Players.Length > 0)
        {
            for (int i = 0; i < cards.Length; i++)
            {
                objThis.Deck.MoveTo(cards[i], objThis.CurrentPlayer.Hand);

                objThis.AdvancePlayer();
            }
        }

        return objThis;
    }

public static Card[] MoveTo(this Card[] objThis, Card card, Card[] cards)
    {
        List<Card> lstCards = cards.ToList();
        List<Card> lstThis = objThis.ToList();

        lstThis.Remove(card);
        lstCards.Add(card);

        objThis = lstThis.ToArray();
        cards = lstCards.ToArray();

        return cards;
    }

当然你可以看到参考问题。使用ref关键字会导致一些看起来不太好看的代码,但它可能是不可避免的。有什么建议吗?

我更喜欢一种足够灵活的解决方案,可以处理其他“卡片传递”情况(玩家将牌打到堆中,将牌从堆中移到“垃圾”牌组等)。

3 个答案:

答案 0 :(得分:3)

对于Arrays,这是一个不好的情况,我认为,这通常不是为了重复添加和删除而设计的。另外,我不会将其作为扩展方法,因为它与您应用程序中的几个选定位置之外没有任何关联。

考虑只使用List而不是使用一个负责移动的类方法。

public class CardDealer {
...
  private List<Card> _deck;

  // Put the card [c] into [hand], and remove it from the deck.
  public void Deal(List<Card> hand, Card c) {
    _deck.Remove(c);
    hand.Add(c);
  }
}

评论者建议,一副牌可以更好地建模为队列,这是一个合法的点,取决于你是否只能从牌组的顶部取卡。如果情况确实如此,请考虑一下:

public class CardDealer {
...
  private Queue<Card> _deck;

  // Put the top card of the deck into the specified hand.
  public void Deal(List<Card> hand) {
    // Deck is a Queue now. No need to specify which card to take.
    Card c = _deck.Dequeue(); 
    hand.Add(c);
  }
}

答案 1 :(得分:1)

嗯,一个简单的方法是首先不使用数组。从一开始就使用列表,您不需要重新分配等 - 只需从卡座中移除并添加到手中。您可能想考虑使用Queue<T>作为套牌。

使用不可变集合和ref参数的功能更多,但如果没有一些好的不可变集合类,那么这种方法并不十分实用。 (它们可用,但没有内置到框架中。)

为什么要将卡片组传递给方法呢?它不应该只是处理甲板上的所有东西吗?此时写起来更容易:

foreach (Card card in deck)
{
    CurrentPlayer.Hand.Add(card);
    AdvancePlayer();
}
deck.Clear();

(我不确定你为什么在这里使用扩展方法,顺便说一句。这看起来像是一个更合适的实例方法。)

答案 2 :(得分:0)

也许是这样的?

interface ICardPile
{
    ICollection<Card> Cards
    {
        get;
    }
}

interface IOrderedCardPile : ICardPile // FIXME Better name.
{
}

class Deck : ICardPile
{
    private Stack<Card> _cards = new Stack<Card>();

    ICollection<Card> Cards
    {
        get
        {
            return _cards;
        }
    }

    public Deck()
    {
        // TODO Fill deck.
    }

    public void DealCardsTo(IEnumerable<ICardPile> piles, int cardCount)
    {
        for(int i = 0; i < cardCount; ++i)
        {
            foreach(var pile in piles)
                Cards.MoveSomeTo(piles, 1);
        }
    }
}

class Hand : IOrderedCardPile
{
    private HashSet<Card> _cards = new HashSet<Card>();

    ICollection<Card> Cards
    {
        get
        {
            return _cards;
        }
    }
}

// Extension methods
static void MoveSomeTo(this ICardPile pile, ICardPile other, int count)
{
    // Removes cards from the end of pile and puts them at the end of other.

    foreach(Card card in pile.Cards.Reverse().Take(count))
    {
        other.Add(card);
    }

    pile.Cards = pile.Cards.Take(count);
}

static void MoveCardTo(this IOrderedCardPile pile, ICardPile other, Card card)
{
    // Removes card from pile and puts it at the end of other.
    pile.Remove(card);
    other.Add(card);
}

// Examples
Deck deck;
DiscardPile discard;
var hands = new Hand[4];

deck.DealCardsTo(hands, 7);

// Discard all aces.
forach(var hand in hands)
{
    foreach(var card in hand.Cards.Where(card => card.Number == Card.SomeEnum.Ace))
        hand.MoveCardTo(discard, card);
}