Use the types you have
Why are you storing the card value as an int? You have already created a CardValue
enum, so use it. Change the Card.CardValue
property and the cardValue
parameter in the Card
constructor to be of type CardValue
. That way, you don't need to cast to an int
when creating a card, and you can always get the numeric card value by casting ((int)cardValue
) when you need it.
Argument Validation
What happens if you use new Card((CardSuit)100, (CardValue)100)
. Your card will have an invalid suit and value. In order to validate the arguments, you can use Enum.IsDefined
, which will cause a performance penalty since it uses reflection, or you can hardcode a valid range of values.
Type Naming
The name CardValue
seems too general for such a specific type (thanks to @RickDavin). "Value" implies it may refer to any value from the card, such as its suit or rank. A better name might be CardRank
.
Property Naming
As noted by @Heslacher, type names shouldn't be used as property names. For instance, in the Card
class, the property CardSuit
and has a redundant name. Consider a variable Card card = new Card()
. Using card.Suit
is much clearer and more concise than using card.CardSuit
. In addition, you should change the property CardValue
to Rank
, since the type CardValue
was renamed.
Readonly Properties
It seems Rank
and Suit
are supposed to be readonly. Prior to C#6, your approach was an acceptable one, so long as you remembered not to change those properties outside of the constructor. However, if you're using C#6, a better alternative is to declare them without setters. Like readonly
fields, properties that have only getters can only be set inside the constructor, but not outside of it.
With that, the Card
class should look like:
public class Card
{
public CardSuit Suit { get; }
public CardRank Rank { get; }
public Card(CardSuit suit, CardRank rank)
{
if (suit < CardSuit.Spades || suit > CardSuit.Clubs) // or if (!Enum.IsDefined(typeof(CardSuit), suit))
{
// handle error (possibly with ArgumentException)
}
if (rank < CardRank.Ace || rank > CardRank.King) // or if (!Enum.IsDefined(typeof(CardValue), rank))
{
// handle error
}
Suit = suit;
Rank = rank;
}
}
identifier
might be better. The Game rules will decide value by mapping ids to values, ordinal or otherwise. \$\endgroup\$