Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non-generic StringComparer conversion for dict and hashset #49608

Merged
merged 5 commits into from
Mar 15, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public Dictionary(int capacity, IEqualityComparer<TKey>? comparer)
Initialize(capacity);
}

if (comparer != null && comparer != EqualityComparer<TKey>.Default) // first check for null to avoid forcing default comparer instantiation unnecessarily
if (comparer is not null && comparer != EqualityComparer<TKey>.Default) // first check for null to avoid forcing default comparer instantiation unnecessarily
{
_comparer = comparer;
}
Expand All @@ -65,18 +65,11 @@ public Dictionary(int capacity, IEqualityComparer<TKey>? comparer)

if (typeof(TKey) == typeof(string))
{
if (_comparer is null)
{
_comparer = (IEqualityComparer<TKey>)NonRandomizedStringEqualityComparer.WrappedAroundDefaultComparer;
}
else if (ReferenceEquals(_comparer, StringComparer.Ordinal))
{
_comparer = (IEqualityComparer<TKey>)NonRandomizedStringEqualityComparer.WrappedAroundStringComparerOrdinal;
}
else if (ReferenceEquals(_comparer, StringComparer.OrdinalIgnoreCase))
{
_comparer = (IEqualityComparer<TKey>)NonRandomizedStringEqualityComparer.WrappedAroundStringComparerOrdinalIgnoreCase;
}
// We know TKey is string from above check so Unsafe.As from
// the generic and then from string back to the generic
_comparer = Unsafe.As<IEqualityComparer<TKey>>(
NonRandomizedStringEqualityComparer.GetStringComparer(
Unsafe.As<IEqualityComparer<string>>(_comparer)));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public HashSet() : this((IEqualityComparer<T>?)null) { }

public HashSet(IEqualityComparer<T>? comparer)
{
if (comparer != null && comparer != EqualityComparer<T>.Default) // first check for null to avoid forcing default comparer instantiation unnecessarily
if (comparer is not null && comparer != EqualityComparer<T>.Default) // first check for null to avoid forcing default comparer instantiation unnecessarily
{
_comparer = comparer;
}
Expand All @@ -65,18 +65,11 @@ public HashSet(IEqualityComparer<T>? comparer)

if (typeof(T) == typeof(string))
{
if (_comparer is null)
{
_comparer = (IEqualityComparer<T>)NonRandomizedStringEqualityComparer.WrappedAroundDefaultComparer;
}
else if (ReferenceEquals(_comparer, StringComparer.Ordinal))
{
_comparer = (IEqualityComparer<T>)NonRandomizedStringEqualityComparer.WrappedAroundStringComparerOrdinal;
}
else if (ReferenceEquals(_comparer, StringComparer.OrdinalIgnoreCase))
{
_comparer = (IEqualityComparer<T>)NonRandomizedStringEqualityComparer.WrappedAroundStringComparerOrdinalIgnoreCase;
}
// We know T is string from above check so Unsafe.As from
// the generic and then from string back to the generic
_comparer = Unsafe.As<IEqualityComparer<T>>(
NonRandomizedStringEqualityComparer.GetStringComparer(
Unsafe.As<IEqualityComparer<string>>(_comparer)));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;
using Internal.Runtime.CompilerServices;

namespace System.Collections.Generic
{
Expand All @@ -20,9 +18,9 @@ public class NonRandomizedStringEqualityComparer : IEqualityComparer<string?>, I
// that was passed in to the ctor. The caller chooses one of these singletons so that the
// GetUnderlyingEqualityComparer method can return the correct value.

internal static readonly NonRandomizedStringEqualityComparer WrappedAroundDefaultComparer = new OrdinalComparer(EqualityComparer<string?>.Default);
internal static readonly NonRandomizedStringEqualityComparer WrappedAroundStringComparerOrdinal = new OrdinalComparer(StringComparer.Ordinal);
internal static readonly NonRandomizedStringEqualityComparer WrappedAroundStringComparerOrdinalIgnoreCase = new OrdinalIgnoreCaseComparer(StringComparer.OrdinalIgnoreCase);
private static readonly NonRandomizedStringEqualityComparer WrappedAroundDefaultComparer = new OrdinalComparer(EqualityComparer<string?>.Default);
private static readonly NonRandomizedStringEqualityComparer WrappedAroundStringComparerOrdinal = new OrdinalComparer(StringComparer.Ordinal);
private static readonly NonRandomizedStringEqualityComparer WrappedAroundStringComparerOrdinalIgnoreCase = new OrdinalIgnoreCaseComparer(StringComparer.OrdinalIgnoreCase);

private readonly IEqualityComparer<string?> _underlyingComparer;

Expand Down Expand Up @@ -109,5 +107,26 @@ internal override RandomizedStringEqualityComparer GetRandomizedEqualityComparer
return RandomizedStringEqualityComparer.Create(_underlyingComparer, ignoreCase: true);
}
}

public static IEqualityComparer<string> GetStringComparer(IEqualityComparer<string>? comparer)
{
// Special-case EqualityComparer<string>.Default, StringComparer.Ordinal, and StringComparer.OrdinalIgnoreCase.
// We use a non-randomized comparer for improved perf, falling back to a randomized comparer if the
// hash buckets become unbalanced.
if (comparer is null)
{
comparer = WrappedAroundDefaultComparer;
}
else if (ReferenceEquals(comparer, StringComparer.Ordinal))
{
comparer = WrappedAroundStringComparerOrdinal;
}
else if (ReferenceEquals(comparer, StringComparer.OrdinalIgnoreCase))
{
comparer = WrappedAroundStringComparerOrdinalIgnoreCase;
}

return comparer;
}
}
}