What's wrong
ExceptWith in all three sets loops foreach (T item in other) Remove(item);. When other is the set itself, the set is modified while it is being enumerated:
Containers/ContiguousSet.cs (~line 370): the hand-written enumerator has no version check, and each Remove shifts the array, so every other element is skipped.
Containers/OrderedSet.cs (~line 380) and Containers/InsertionOrderSet.cs (~line 299) enumerate the underlying List<T>, so they throw.
var s = new ContiguousSet<int> { 1, 2, 3, 4 };
s.ExceptWith(s); // Count=2, [2, 4]
var o = new OrderedSet<int> { 1, 2, 3, 4 };
o.ExceptWith(o); // InvalidOperationException: Collection was modified
var i = new InsertionOrderSet<int> { 1, 2, 3, 4 };
i.ExceptWith(i); // InvalidOperationException
var h = new HashSet<int> { 1, 2, 3, 4 };
h.ExceptWith(h); // [] (BCL reference behaviour)
Reproduced against the current main build.
Why it matters
These types implement ISet<T>, and HashSet<T>/SortedSet<T> define x.ExceptWith(x) as clearing the set. Code written against ISet<T> (for example "remove everything in selection from items" where both happen to be the same instance) either crashes or silently leaves half the elements behind, depending on which set type it was given.
Suggested fix
At the top of each ExceptWith, after the null check:
if (ReferenceEquals(other, this))
{
Clear();
return;
}
The other set operations already copy other into a temporary set first, so they are not affected.
Acceptance criteria
x.ExceptWith(x) leaves x empty for ContiguousSet, OrderedSet and InsertionOrderSet.
- A regression test per type.
What's wrong
ExceptWithin all three sets loopsforeach (T item in other) Remove(item);. Whenotheris the set itself, the set is modified while it is being enumerated:Containers/ContiguousSet.cs(~line 370): the hand-written enumerator has no version check, and eachRemoveshifts the array, so every other element is skipped.Containers/OrderedSet.cs(~line 380) andContainers/InsertionOrderSet.cs(~line 299) enumerate the underlyingList<T>, so they throw.Reproduced against the current
mainbuild.Why it matters
These types implement
ISet<T>, andHashSet<T>/SortedSet<T>definex.ExceptWith(x)as clearing the set. Code written againstISet<T>(for example "remove everything inselectionfromitems" where both happen to be the same instance) either crashes or silently leaves half the elements behind, depending on which set type it was given.Suggested fix
At the top of each
ExceptWith, after the null check:The other set operations already copy
otherinto a temporary set first, so they are not affected.Acceptance criteria
x.ExceptWith(x)leavesxempty forContiguousSet,OrderedSetandInsertionOrderSet.