Skip to content

Commit

Permalink
[release/8.0-rc1] Fix ToFrozenDictionary(selectors) on empty sources (#…
Browse files Browse the repository at this point in the history
…90599)

* Fix ToFrozenDictionary(selectors) on empty sources

As part of optimizing construction, the empty check when using ToFrozenDictionary taking selector delegates was skipped, leading to later failures when an empty source is used.

* Address PR feedback

---------

Co-authored-by: Stephen Toub <stoub@microsoft.com>
  • Loading branch information
github-actions[bot] and stephentoub authored Aug 15, 2023
1 parent c5aba1b commit 9607d25
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static FrozenDictionary<TKey, TValue> ToFrozenDictionary<TKey, TValue>(th
public static FrozenDictionary<TKey, TSource> ToFrozenDictionary<TSource, TKey>(
this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer = null)
where TKey : notnull =>
CreateFromDictionary(source.ToDictionary(keySelector, comparer));
source.ToDictionary(keySelector, comparer).ToFrozenDictionary(comparer);

/// <summary>Creates a <see cref="FrozenDictionary{TKey, TElement}"/> from an <see cref="IEnumerable{TSource}"/> according to specified key selector and element selector functions.</summary>
/// <typeparam name="TSource">The type of the elements of <paramref name="source"/>.</typeparam>
Expand All @@ -55,7 +55,7 @@ public static FrozenDictionary<TKey, TSource> ToFrozenDictionary<TSource, TKey>(
public static FrozenDictionary<TKey, TElement> ToFrozenDictionary<TSource, TKey, TElement>(
this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey>? comparer = null)
where TKey : notnull =>
CreateFromDictionary(source.ToDictionary(keySelector, elementSelector, comparer));
source.ToDictionary(keySelector, elementSelector, comparer).ToFrozenDictionary(comparer);

/// <summary>
/// Extracts from the source either an existing <see cref="FrozenDictionary{TKey,TValue}"/> instance or a <see cref="Dictionary{TKey,TValue}"/>
Expand Down Expand Up @@ -113,6 +113,8 @@ public static FrozenDictionary<TKey, TElement> ToFrozenDictionary<TSource, TKey,
private static FrozenDictionary<TKey, TValue> CreateFromDictionary<TKey, TValue>(Dictionary<TKey, TValue> source)
where TKey : notnull
{
Debug.Assert(source.Count > 0, "Empty sources should have been filtered out by caller");

IEqualityComparer<TKey> comparer = source.Comparer;

// Optimize for value types when the default comparer is being used. In such a case, the implementation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public static FrozenSet<T> ToFrozenSet<T>(this IEnumerable<T> source, IEqualityC

private static FrozenSet<T> CreateFromSet<T>(HashSet<T> source)
{
Debug.Assert(source.Count > 0, "Empty sources should have been filtered out by caller");

IEqualityComparer<T> comparer = source.Comparer;

// Optimize for value types when the default comparer is being used. In such a case, the implementation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,35 @@ public void NullSource_ThrowsException()
[Fact]
public void EmptySource_ProducedFrozenDictionaryEmpty()
{
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, new Dictionary<TKey, TValue>().ToFrozenDictionary());
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, Enumerable.Empty<KeyValuePair<TKey, TValue>>().ToFrozenDictionary());
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, Array.Empty<KeyValuePair<TKey, TValue>>().ToFrozenDictionary());
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, new List<KeyValuePair<TKey, TValue>>().ToFrozenDictionary());
IEnumerable<KeyValuePair<TKey, TValue>>[] sources = new[]
{
new Dictionary<TKey, TValue>(),
Enumerable.Empty<KeyValuePair<TKey, TValue>>(),
Array.Empty<KeyValuePair<TKey, TValue>>(),
new List<KeyValuePair<TKey, TValue>>()
};

foreach (IEqualityComparer<TKey> comparer in new IEqualityComparer<TKey>[] { null, EqualityComparer<TKey>.Default })
foreach (IEnumerable<KeyValuePair<TKey, TValue>> source in sources)
{
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, new Dictionary<TKey, TValue>().ToFrozenDictionary(comparer));
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, Enumerable.Empty<KeyValuePair<TKey, TValue>>().ToFrozenDictionary(comparer));
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, Array.Empty<KeyValuePair<TKey, TValue>>().ToFrozenDictionary(comparer));
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, new List<KeyValuePair<TKey, TValue>>().ToFrozenDictionary(comparer));
}
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, source.ToFrozenDictionary());
Assert.Same(FrozenDictionary<TKey, KeyValuePair<TKey, TValue>>.Empty, source.ToFrozenDictionary(s => s.Key));
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, source.ToFrozenDictionary(s => s.Key, s => s.Value));

Assert.NotSame(FrozenDictionary<TKey, TValue>.Empty, new Dictionary<TKey, TValue>().ToFrozenDictionary(NonDefaultEqualityComparer<TKey>.Instance));
Assert.NotSame(FrozenDictionary<TKey, TValue>.Empty, Enumerable.Empty<KeyValuePair<TKey, TValue>>().ToFrozenDictionary(NonDefaultEqualityComparer<TKey>.Instance));
Assert.NotSame(FrozenDictionary<TKey, TValue>.Empty, Array.Empty<KeyValuePair<TKey, TValue>>().ToFrozenDictionary(NonDefaultEqualityComparer<TKey>.Instance));
Assert.NotSame(FrozenDictionary<TKey, TValue>.Empty, new List<KeyValuePair<TKey, TValue>>().ToFrozenDictionary(NonDefaultEqualityComparer<TKey>.Instance));
foreach (IEqualityComparer<TKey> comparer in new IEqualityComparer<TKey>[] { null, EqualityComparer<TKey>.Default })
{
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, source.ToFrozenDictionary(comparer));
Assert.Same(FrozenDictionary<TKey, KeyValuePair<TKey, TValue>>.Empty, source.ToFrozenDictionary(s => s.Key, comparer));
Assert.Same(FrozenDictionary<TKey, TValue>.Empty, source.ToFrozenDictionary(s => s.Key, s => s.Value, comparer));
}

Assert.NotSame(FrozenDictionary<TKey, TValue>.Empty, source.ToFrozenDictionary(NonDefaultEqualityComparer<TKey>.Instance));
Assert.NotSame(FrozenDictionary<TKey, KeyValuePair<TKey, TValue>>.Empty, source.ToFrozenDictionary(s => s.Key, NonDefaultEqualityComparer<TKey>.Instance));
Assert.NotSame(FrozenDictionary<TKey, TValue>.Empty, source.ToFrozenDictionary(s => s.Key, s => s.Value, NonDefaultEqualityComparer<TKey>.Instance));

Assert.Equal(0, source.ToFrozenDictionary(NonDefaultEqualityComparer<TKey>.Instance).Count);
Assert.Equal(0, source.ToFrozenDictionary(s => s.Key, NonDefaultEqualityComparer<TKey>.Instance).Count);
Assert.Equal(0, source.ToFrozenDictionary(s => s.Key, s => s.Value, NonDefaultEqualityComparer<TKey>.Instance).Count);
}
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,24 @@ public void NullSource_ThrowsException()
[Fact]
public void EmptySource_ProducedFrozenSetEmpty()
{
Assert.Same(FrozenSet<T>.Empty, new List<T>().ToFrozenSet());
Assert.Same(FrozenSet<T>.Empty, Enumerable.Empty<T>().ToFrozenSet());
Assert.Same(FrozenSet<T>.Empty, Array.Empty<T>().ToFrozenSet());
Assert.Same(FrozenSet<T>.Empty, new List<T>().ToFrozenSet());
IEnumerable<T>[] sources = new[]
{
new List<T>(),
Enumerable.Empty<T>(),
Array.Empty<T>(),
};

foreach (IEqualityComparer<T> comparer in new IEqualityComparer<T>[] { null, EqualityComparer<T>.Default })
foreach (IEnumerable<T> source in sources)
{
Assert.Same(FrozenSet<T>.Empty, new List<T>().ToFrozenSet(comparer));
Assert.Same(FrozenSet<T>.Empty, Enumerable.Empty<T>().ToFrozenSet(comparer));
Assert.Same(FrozenSet<T>.Empty, Array.Empty<T>().ToFrozenSet(comparer));
Assert.Same(FrozenSet<T>.Empty, new List<T>().ToFrozenSet(comparer));
}
Assert.Same(FrozenSet<T>.Empty, source.ToFrozenSet());

foreach (IEqualityComparer<T> comparer in new IEqualityComparer<T>[] { null, EqualityComparer<T>.Default })
{
Assert.Same(FrozenSet<T>.Empty, source.ToFrozenSet(comparer));
}

Assert.NotSame(FrozenSet<T>.Empty, new List<T>().ToFrozenSet(NonDefaultEqualityComparer<T>.Instance));
Assert.NotSame(FrozenSet<T>.Empty, Enumerable.Empty<T>().ToFrozenSet(NonDefaultEqualityComparer<T>.Instance));
Assert.NotSame(FrozenSet<T>.Empty, Array.Empty<T>().ToFrozenSet(NonDefaultEqualityComparer<T>.Instance));
Assert.NotSame(FrozenSet<T>.Empty, new List<T>().ToFrozenSet(NonDefaultEqualityComparer<T>.Instance));
Assert.NotSame(FrozenSet<T>.Empty, source.ToFrozenSet(NonDefaultEqualityComparer<T>.Instance));
}
}

[Fact]
Expand Down

0 comments on commit 9607d25

Please sign in to comment.