Reputation: 2115
I want to get only distinct values from HashSet
, I have implemented IEquatable
, IEqualityComparer
, but still can't get distinct values.
class Program
{
static void Main(string[] args)
{
HashSet<Item> items = new HashSet<Item>()
{
{new Item("item1")},
{new Item("item2")},
{new Item("item3")},
{new Item("item1")}
};
foreach (var item in items.Distinct())
{
Console.WriteLine(item.Name);
}
Console.ReadKey();
}
}
class Item : IEquatable<Item>, IEqualityComparer<Item>
{
public string Name { get; set; }
public Item(string name)
{
this.Name = name;
}
public bool Equals(Item other)
{
return this.Name.Equals(other.Name);
}
public bool Equals(Item x, Item y)
{
return x.Equals(y);
}
public int GetHashCode(Item obj)
{
return this.Name.GetHashCode();
}
}
Console output:
item1
item2
item3
item1
Thanks!
Upvotes: 0
Views: 1894
Reputation: 149518
If you only have a single implementation which defines equality of your class, implementing IEquatable<T>
(properly) is enough. You shouldn't implement IEqualityComparer<T>
as well. The latter is meant to be, usually as in a separate class, when you want to provide multiple ways to define uniqueness between two elements of a type.
Other than that, Your method signature for GetHashCode
is wrong. Currently, it defers to object.Equals
instead of your custom implementation.
You need to add the override
keyword to your GetHashCode
implementation and remove the Item obj
from the signature:
public override int GetHashCode()
{
return this.Name.GetHashCode();
}
And also override object.Equals
as well to use your Equals(item other)
:
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Item) obj);
}
public override bool Equals(
You don't need to call Distinct()
on a HashSet<T>
as it by itself guarantees uniqueness in its internal collection, given that you provide a proper IEquatable<T>
override or supply it with an IEqualityComparer<T>
.
From the docs of HashSet<T>.Add
:
Return Value:
Type: System.Boolean true if the element is added to the HashSet object; false if the element is already present.
Upvotes: 3
Reputation: 64933
First of all, you should implement IEqualityComparer<T>
as a separate class and you need to provide the whole equality comparer during HashSet<T>
construction:
var set = new HashSet<CustomClass>(new CustomClassEqualityComparer());
If you go with the equality comparer way, you aren't forced to implement IEquatable<T>
:
public class ItemEqualityComparer : IEqualityComparer<Item>
{
public bool Equals(Item x, Item y)
{
return x.Name == y.Name;
}
public int GetHashCode(Item obj)
{
return obj.Name.GetHashCode();
}
}
Furthermore, you can design many IEqualityComparer<T>
implementations to cover many use cases that can define different uniqueness meanings to the same object (i.e. Item
).
If you provide a good implementation of IEqualityComparer<T>
, you won't need Distinct
since a HashSet<T>
is a set and this means that it's an unordered collection of unique elements, and the whole set will use your equality comparer to check if a given element is present in the set (thus, all elements are unique in the same set!).
Upvotes: 2