Equatable collections

edit

The immutable collections of .NET compare by reference: two ImmutableArray<T> with the same items are not equal. A record that contains one loses its value equality, which breaks caches keyed by the record, and the incremental pipelines of source generators.

ImmutableEquatableArray<T>, ImmutableEquatableSet<T> and ImmutableEquatableDictionary<TKey, TValue> are immutable collections that are equal when their items are equal:

var first = new[] { "a", "b" }.ToImmutableEquatableArray();
var second = new List<string> { "a", "b" }.ToImmutableEquatableArray();

// Two arrays with the same items are equal and have the same hash code
Assert.Equal(first, second);
Assert.True(first == second);
Assert.Equal(first.GetHashCode(), second.GetHashCode());

// A record that contains the array gets value equality too
Assert.Equal(new Model("name", first), new Model("name", second));
private sealed record Model(string Name, ImmutableEquatableArray<string> Items);

Create them

CollectionCreated with
ImmutableEquatableArray<T>ToImmutableEquatableArray(), ImmutableEquatableArray.Create, ImmutableEquatableArray<T>.Empty
ImmutableEquatableSet<T>ToImmutableEquatableSet(), ImmutableEquatableSet.Create, ImmutableEquatableSet<T>.Empty
ImmutableEquatableDictionary<TKey, TValue>ToImmutableEquatableDictionary() from pairs, tuples or with key and value selectors, ImmutableEquatableDictionary.Empty<TKey, TValue>()

The items, and the keys and values of the dictionary, must implement IEquatable<T>. Creating a collection from an empty collection returns the shared Empty instance.

Equality

  • The array is equal to another array with the same items in the same order.
  • The set and the dictionary are equal to another set or dictionary with the same items, whatever their order.
  • A null value of the dictionary is equal to another null value.
  • ==, !=, Equals and GetHashCode all follow these rules. The hash code combines the hash codes of the items, so collections with different items rarely share one, and is computed once.

They implement the read-only collection interfaces (IReadOnlyList<T>, IReadOnlySet<T>, IReadOnlyDictionary<TKey, TValue> and the others) and the mutable ones (IList<T>, ISet<T>, IDictionary<TKey, TValue>), whose methods that change the collection throw a NotSupportedException.