I have spent two days chasing down an evil bug. I narrowed down the problem with this test case demonstrating different behavior for different collection classes that implement the same interface. Specifically Contains(null)
throws a NullArgumentException
for ImmutableSortedSet
but not for Array
or the mutable SortedSet
.
using System.Collections.Generic;
using System.Collections.Immutable;
using Xunit;
public class SortedSetSpec
{
[Fact]
public void WTFNullCheck()
{
var data = new[] {"a", "b", "c", "d"};
SortedSet<string> ss = new SortedSet<string>(data);
ImmutableSortedSet<string> iss = ss.ToImmutableSortedSet();
// This passes
data.Contains(null).Should().BeFalse();
// This passes
ss.Contains(null).Should().BeFalse();
// This throws an exception
iss.Contains(null).Should().BeFalse();
}
}
Given that all classes Array / SortedSet and ImmutableSortedSet implement ICollection<T>
shouldn't they have the same behavior on the Contains(null)
call?
The bug manifested itself when I data bound an ImmutableSortedSet
to the ItemsSource
property of a listbox.
<ListBox x:Name="ListBox" Margin="2"
ItemsSource="{Binding WorkflowTags}"
SelectedItem="{Binding SelectedTag}" >
</ListBox>
The problem is that at some point deep in the databinding code ListBox (aka Selector ) asks the collection Contains(null)
?? and then it crashes if I've databound an ImmutableSortedSet
.
So is this a bug with ImmutableCollections
or with WPF
or is it expected behavior and I should know better than to use ImmutableSortedSet
?
The code within ImmutableSortedSet
that is causing the problem can be found in the corefx github repo
/// <summary>
/// See the <see cref="IImmutableSet{T}"/> interface.
/// </summary>
public bool Contains(T value)
{
Requires.NotNullAllowStructs(value, nameof(value));
return _root.Contains(value, _comparer);
}