I am using C# record
s in a HashSet to enforce uniqueness. This works well, but as soon as the record contains a Collection then it stops working.
ie. this passes:
public record Person
{
public string FirstName { get; set; }
}
HashSet<Person> uniquePeople = new();
uniquePeople.Add(new Person { FirstName = "John" });
uniquePeople.Add(new Person { FirstName = "John" });
Assert.That(uniquePeople.Count() == 1);
But this fails:
public record Person
{
public string FirstName { get; set; }
public HashSet<int> FavouriteNumbers { get; set; } = new();
}
HashSet<Person> uniquePeople = new();
uniquePeople.Add(new Person { FirstName = "John" });
uniquePeople.Add(new Person { FirstName = "John" });
Assert.That(uniquePeople.Count() == 1);
How can I enforce uniqueness on nested collections using records?
You want custom Equals
and GetHashCode
since you want to compare FavouriteNumbers
as sets, not as references; that's why I suggest turning record
(which has its own implementation of these methods) into class
:
// Note class, not record
public class Person : IEquatable<Person> {
public string FirstName { get; set; }
public HashSet<int> FavouriteNumbers { get; set; } = new();
public override int GetHashCode() =>
HashCode.Combine(FirstName, FavouriteNumbers.Count);
public bool Equals(Person? other) => other is not null &&
string.Equals(FirstName, other.FirstName) &&
FavouriteNumbers.SetEquals(other.FavouriteNumbers);
public override bool Equals(object? obj) => Equals(obj as Person);
public override string ToString() => $"{FirstName}";
}
If you have to deal with record
(you can't change it), you can implement custom comparer:
public sealed class PersonEqualityComparer : IEqualityComparer<Person> {
public bool Equals(Person? x, Person? y) {
if (ReferenceEquals(x, y))
return true;
if (x is null || y is null)
return false;
return string.Equals(x.FirstName, y.FirstName) &&
x.FavouriteNumbers.SetEquals(y.FavouriteNumbers);
}
public int GetHashCode(Person obj) =>
obj is null ? 0 : HashCode.Combine(obj.FirstName, obj.FavouriteNumbers.Count);
}
and then use it:
// note PersonEqualityComparer
HashSet<Person> uniquePeople = new(new PersonEqualityComparer());
uniquePeople.Add(new Person { FirstName = "John" });
uniquePeople.Add(new Person { FirstName = "John" });