开发者

Unique objects by Property using LINQ C#

I wish to retrieve the unique items from a list using LINQ in C#. I have a class Deck, with a property Cards which is a list of Card. Furthermore, the class Card has a property Suite of type Suite. I want to retreive all the Suite items in the List of Card, but only the unique suites. The code I have tried is:

List<Suite> GetSuiteByCards(List<Card> 开发者_如何学编程cards)
    {
        List<Suite> list = cards.Select(c => c.Suite).Distinct().ToList();
        return list;
    }

If I am not mistaken, this should retrieve all the unique suites from the list of cards. However, the following test fails

// Creating test cards
Card a = new Card(suite="spade", value="ace");
Card b = new Card(suite="spade", value="king");
List<Card> cards = new List<Card>(2) {a, b}; 

// Creating deck of cards
Deck d = new Deck(cards);

The above code creates a small deck of cards (2 cards), with the same suite. So, the following code should evaluate true:

d.Suites.Count == 1

except that I am getting:

d.Suites.Count == 2

I have ommitted some code, but I don't think it is relevant. If I run the code with a full deck (i.e. 52 cards) I get 4 unique Suites, as expected; thus I am quite baffled.

I have resolved the issue through an extension method, but I was wondering if there was a simple way to do it via LINQ.


the class Card has a property Suite of type Suite

Psychic debugging powers on. I am guessing that Suite is a reference type, and you didn't override Object.Equals and Object.GetHashCode.

You need to override Object.Equals and Object.GetHashCode for Suite. Distinct uses EqualityComparer<Suite>.Default if you don't provide an implementation of IEqualityComparer<Suite>. The implementation returned by EqualityComparer<Suite>.Default uses Object.Equals and Object.GetHashCode. So, if you don't override these, then the implementation returned models reference equality (and hash code by reference).

Alternatively, implement IEqualityComparer<Suite> to compare not by reference equality and pass an instance to Enumerable.Distinct<TSource>(this IEnumerable<TSource>, IEqualityComparer<TSource>).

I have ommitted some code, but I don't think it is relevant. If I run the code with a full deck (i.e. 52 cards) I get 4 unique Suites, as expected; thus I am quite baffled.

It's kind of relevant, but let me take a guess as to what is going on. In your above code, the full listing is probably something more like:

Card a = new Card(new Suite("spade"), new Value("ace"));
Card b = new Card(new Suite("spade"), new Value("king"));
List<Card> cards = new List<Card>(2) {a, b}; 

and so here you are creating two instances of Suite representing spades. However, these two instances will not compare as equal because they are different instances and you haven't overridden Equals (and GetHashCode by necessity).

However, in the case of the 52-card deck, I bet you did something like this:

List<Suite> suites = new List<Suite>() suites {
    new Suite("spade"),
    new Suite("heart"),
    new Suite("club"),
    new Suite("diamond")
};

List<Value> values = new List<Value>() values {
    new Value("ace"),
    new Value("king"),
    // etc.
};

and then

var deck = from suite in suites
           from value in values
           select new Card(suite, value);

and now all the cards the represent a space are sharing the same reference to Suite and thus Distinct is okay.

By the way, I wouldn't model suits and values as reference types anyway. They really are value types. A heart is a heart, and that is its identity. It's identity is not defined by who it is, but rather by what it is.

This would obviate the whole problem to begin with anyway, since the default equality operator for value types is value equality.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜