diff --git a/src/Compilers/Core/Portable/InternalUtilities/EmptyComparer.cs b/src/Compilers/Core/Portable/InternalUtilities/EmptyComparer.cs
deleted file mode 100644
index 0f61efb45d893..0000000000000
--- a/src/Compilers/Core/Portable/InternalUtilities/EmptyComparer.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Collections.Generic;
-using Microsoft.CodeAnalysis;
-
-namespace Roslyn.Utilities
-{
- ///
- /// Very cheap trivial comparer that never matches the keys,
- /// should only be used in empty dictionaries.
- ///
- internal sealed class EmptyComparer : IEqualityComparer
- {
- public static readonly EmptyComparer Instance = new EmptyComparer();
-
- private EmptyComparer()
- {
- }
-
- bool IEqualityComparer.Equals(object? a, object? b)
- => throw ExceptionUtilities.Unreachable();
-
- int IEqualityComparer.GetHashCode(object s)
- {
- // dictionary will call this often
- return 0;
- }
- }
-}
diff --git a/src/Dependencies/Contracts/EqualityComparerExtensions.cs b/src/Dependencies/Contracts/EqualityComparerExtensions.cs
new file mode 100644
index 0000000000000..e67f8320a7aaf
--- /dev/null
+++ b/src/Dependencies/Contracts/EqualityComparerExtensions.cs
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#nullable enable
+
+#if !NET8_0_OR_GREATER
+using System.Diagnostics.CodeAnalysis;
+#endif
+
+namespace System.Collections.Generic;
+
+internal static class RoslynEqualityComparerExtensions
+{
+#if NET8_0_OR_GREATER
+
+ // for binary compatibility
+ public static EqualityComparer Create(Func equals, Func? getHashCode = null)
+ => EqualityComparer.Create(equals, getHashCode);
+
+#else
+ // Implementation based on https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/EqualityComparer.cs
+
+ extension(EqualityComparer)
+ {
+ ///
+ /// Creates an by using the specified delegates as the implementation of the comparer's
+ /// and methods.
+ ///
+ /// The delegate to use to implement the method.
+ ///
+ /// The delegate to use to implement the method.
+ /// If no delegate is supplied, calls to the resulting comparer's
+ /// will throw .
+ ///
+ /// The new comparer.
+ public static EqualityComparer Create(Func equals, Func? getHashCode = null)
+ {
+ getHashCode ??= _ => throw new NotSupportedException();
+ return new DelegateEqualityComparer(equals, getHashCode);
+ }
+ }
+
+ private sealed class DelegateEqualityComparer(Func equals, Func getHashCode) : EqualityComparer
+ {
+ private readonly Func _equals = equals;
+ private readonly Func _getHashCode = getHashCode;
+
+ public override bool Equals(T? x, T? y) =>
+ _equals(x, y);
+
+ public override int GetHashCode(T obj) =>
+ _getHashCode(obj);
+
+ public override bool Equals(object? obj)
+ => obj is DelegateEqualityComparer other && _equals == other._equals && _getHashCode == other._getHashCode;
+
+ public override int GetHashCode()
+ => unchecked(_equals.GetHashCode() * (int)0xA5555529 + _getHashCode.GetHashCode());
+ }
+#endif
+}
+
diff --git a/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems b/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems
index e3725f02a07f8..504f3c43ac0b3 100644
--- a/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems
+++ b/src/Dependencies/Contracts/Microsoft.CodeAnalysis.Contracts.projitems
@@ -25,6 +25,8 @@
+
+
diff --git a/src/Compilers/Core/Portable/InternalUtilities/ReferenceEqualityComparer.cs b/src/Dependencies/Contracts/ReferenceEqualityComparer.cs
similarity index 99%
rename from src/Compilers/Core/Portable/InternalUtilities/ReferenceEqualityComparer.cs
rename to src/Dependencies/Contracts/ReferenceEqualityComparer.cs
index 8176987e9e9cd..ce160297459c0 100644
--- a/src/Compilers/Core/Portable/InternalUtilities/ReferenceEqualityComparer.cs
+++ b/src/Dependencies/Contracts/ReferenceEqualityComparer.cs
@@ -4,7 +4,9 @@
using System.Runtime.CompilerServices;
-#if NET5_0_OR_GREATER
+#nullable enable
+
+#if NET
#pragma warning disable RS0016 // Add public types and members to the declared API (this is a supporting forwarder for an internal polyfill API)
[assembly: TypeForwardedTo(typeof(System.Collections.Generic.ReferenceEqualityComparer))]
diff --git a/src/EditorFeatures/Test/Collections/Immutable/Maps/MapTests.cs b/src/EditorFeatures/Test/Collections/Immutable/Maps/MapTests.cs
index 1908f55551332..c7e93e8b55e96 100644
--- a/src/EditorFeatures/Test/Collections/Immutable/Maps/MapTests.cs
+++ b/src/EditorFeatures/Test/Collections/Immutable/Maps/MapTests.cs
@@ -82,7 +82,9 @@ public void TestRemove()
[Fact]
public void TestPathology()
{
- var map = ImmutableDictionary.Create(new PathologicalComparer())
+ var comparer = EqualityComparer.Create(EqualityComparer.Default.Equals, _ => 0);
+
+ var map = ImmutableDictionary.Create(comparer)
.Add("1", 1)
.Add("2", 2)
.Add("3", 3)
@@ -118,13 +120,4 @@ public void TestPathology()
map = map.Remove("5");
Assert.Empty(map);
}
-
- private sealed class PathologicalComparer : IEqualityComparer
- {
- public bool Equals(T x, T y)
- => EqualityComparer.Default.Equals(x, y);
-
- public int GetHashCode(T obj)
- => 0;
- }
}
diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs
index 84fe32c5f955d..2c44eb5daa37e 100644
--- a/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs
+++ b/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs
@@ -19,7 +19,9 @@ namespace Microsoft.CodeAnalysis.Completion.Providers;
internal abstract partial class AbstractKeywordCompletionProvider : LSPCompletionProvider
where TContext : SyntaxContext
{
- private static readonly Comparer s_comparer = new();
+ private static readonly EqualityComparer s_comparer = EqualityComparer.Create(
+ static (x, y) => x?.DisplayText == y?.DisplayText,
+ static x => x.DisplayText.GetHashCode());
private readonly ImmutableArray> _keywordRecommenders;
@@ -72,13 +74,4 @@ private async Task> RecommendKeywordsAsync(
public sealed override Task GetTextChangeAsync(Document document, CompletionItem item, char? ch, CancellationToken cancellationToken)
=> Task.FromResult((TextChange?)new TextChange(item.Span, item.DisplayText));
-
- private sealed class Comparer : IEqualityComparer
- {
- public bool Equals(CompletionItem? x, CompletionItem? y)
- => x?.DisplayText == y?.DisplayText;
-
- public int GetHashCode(CompletionItem obj)
- => Hash.Combine(obj.DisplayText.GetHashCode(), obj.DisplayText.GetHashCode());
- }
}
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems
index 6f9309d886cb3..755a43b260eb3 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CompilerExtensions.projitems
@@ -1,4 +1,4 @@
-
+
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
@@ -19,7 +19,6 @@
-