From fd47a98f31fde4e3d28fec31659ccad5f2a1e18a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 13:33:55 +0000 Subject: [PATCH 1/7] Initial plan From 80ea69f04d98888aadfbd99c1a668c4c97331335 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 13:43:52 +0000 Subject: [PATCH 2/7] Update GetHashCode implementations to use HashCode.Combine Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- ...ing-variance-in-interfaces-for-generic-collections.md | 5 +---- .../snippets/System/Object/Equals/csharp/equals2.cs | 4 ++-- .../snippets/System/Object/Equals/fsharp/equals2.fs | 4 ++-- .../snippets/System/Object/Equals/vb/equals2.vb | 4 ++-- .../snippets/System/Object/GetHashCode/csharp/xor2.cs | 9 +++++---- .../snippets/System/Object/GetHashCode/fsharp/xor2.fs | 9 +++++---- .../snippets/System/Object/GetHashCode/vb/xor2.vb | 9 +++++---- .../runtime-libraries/system-object-gethashcode.md | 4 ++-- ...ing-variance-in-interfaces-for-generic-collections.md | 6 +----- 9 files changed, 25 insertions(+), 29 deletions(-) diff --git a/docs/csharp/programming-guide/concepts/covariance-contravariance/using-variance-in-interfaces-for-generic-collections.md b/docs/csharp/programming-guide/concepts/covariance-contravariance/using-variance-in-interfaces-for-generic-collections.md index 312c9719e4292..490322c2f0010 100644 --- a/docs/csharp/programming-guide/concepts/covariance-contravariance/using-variance-in-interfaces-for-generic-collections.md +++ b/docs/csharp/programming-guide/concepts/covariance-contravariance/using-variance-in-interfaces-for-generic-collections.md @@ -81,10 +81,7 @@ class PersonComparer : IEqualityComparer public int GetHashCode(Person person) { if (Object.ReferenceEquals(person, null)) return 0; - int hashFirstName = person.FirstName == null - ? 0 : person.FirstName.GetHashCode(); - int hashLastName = person.LastName.GetHashCode(); - return hashFirstName ^ hashLastName; + return HashCode.Combine(person.FirstName, person.LastName); } } diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/csharp/equals2.cs b/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/csharp/equals2.cs index 042751f2634d6..e64ae9a67b8f7 100644 --- a/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/csharp/equals2.cs +++ b/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/csharp/equals2.cs @@ -30,7 +30,7 @@ public override bool Equals(Object obj) public override int GetHashCode() { - return (x << 2) ^ y; + return HashCode.Combine(x, y); } public override string ToString() @@ -59,7 +59,7 @@ public override bool Equals(Object obj) public override int GetHashCode() { - return (base.GetHashCode() << 2) ^ z; + return HashCode.Combine(base.GetHashCode(), z); } public override String ToString() diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/fsharp/equals2.fs b/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/fsharp/equals2.fs index fdfe5e871a45a..4724fd1968c0a 100644 --- a/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/fsharp/equals2.fs +++ b/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/fsharp/equals2.fs @@ -15,7 +15,7 @@ type Point(x, y) = false override _.GetHashCode() = - (x <<< 2) ^^^ y + System.HashCode.Combine(x, y) override _.ToString() = $"Point({x}, {y})" @@ -32,7 +32,7 @@ type Point3D(x, y, z) = false override _.GetHashCode() = - (base.GetHashCode() <<< 2) ^^^ z + System.HashCode.Combine(base.GetHashCode(), z) override _.ToString() = $"Point({x}, {y}, {z})" diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/vb/equals2.vb b/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/vb/equals2.vb index 73cadaf12e61c..43dba9f38b9ae 100644 --- a/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/vb/equals2.vb +++ b/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/vb/equals2.vb @@ -23,7 +23,7 @@ Class Point1 End Function Public Overrides Function GetHashCode() As Integer - Return (x << 2) Xor y + Return HashCode.Combine(x, y) End Function Public Overrides Function ToString() As String @@ -49,7 +49,7 @@ Class Point3D : Inherits Point1 End Function Public Overrides Function GetHashCode() As Integer - Return (MyBase.GetHashCode() << 2) Xor z + Return HashCode.Combine(MyBase.GetHashCode(), z) End Function Public Overrides Function ToString() As String diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/csharp/xor2.cs b/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/csharp/xor2.cs index 0330195634a29..d8ad45dd2f9f0 100644 --- a/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/csharp/xor2.cs +++ b/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/csharp/xor2.cs @@ -27,7 +27,7 @@ public override bool Equals(Object obj) public override int GetHashCode() { - return Tuple.Create(x, y).GetHashCode(); + return HashCode.Combine(x, y); } } @@ -42,7 +42,8 @@ public static void Main() Console.WriteLine(pt.GetHashCode()); } } -// The example displays the following output: -// 173 -// 269 +// The example displays output similar to the following. +// Note: HashCode.Combine results are not stable across .NET versions. +// 185727722 +// -363254492 // diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/fsharp/xor2.fs b/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/fsharp/xor2.fs index c83ecf8770421..1290f982eb0ae 100644 --- a/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/fsharp/xor2.fs +++ b/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/fsharp/xor2.fs @@ -14,14 +14,15 @@ type Point(x: int, y: int) = false override _.GetHashCode() = - (x, y).GetHashCode() + System.HashCode.Combine(x, y) let pt = Point(5, 8) printfn $"{pt.GetHashCode()}" let pt2 = Point(8, 5) printfn $"{pt2.GetHashCode()}" -// The example displays the following output: -// 173 -// 269 +// The example displays output similar to the following. +// Note: HashCode.Combine results are not stable across .NET versions. +// 185727722 +// -363254492 // \ No newline at end of file diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/vb/xor2.vb b/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/vb/xor2.vb index 58e5e6afeec69..7a18c7232ed10 100644 --- a/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/vb/xor2.vb +++ b/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/vb/xor2.vb @@ -16,7 +16,7 @@ Public Structure Point End Function Public Overrides Function GetHashCode() As Integer - Return Tuple.Create(x, y).GetHashCode() + Return HashCode.Combine(x, y) End Function End Structure @@ -29,7 +29,8 @@ Public Module Example Console.WriteLine(pt.GetHashCode()) End Sub End Module -' The example displays the following output: -' 173 -' 269 +' The example displays output similar to the following. +' Note: HashCode.Combine results are not stable across .NET versions. +' 185727722 +' -363254492 ' diff --git a/docs/fundamentals/runtime-libraries/system-object-gethashcode.md b/docs/fundamentals/runtime-libraries/system-object-gethashcode.md index 76ca507b4427e..5e28c290748c3 100644 --- a/docs/fundamentals/runtime-libraries/system-object-gethashcode.md +++ b/docs/fundamentals/runtime-libraries/system-object-gethashcode.md @@ -58,13 +58,13 @@ Frequently, a type has multiple data fields that can participate in generating t :::code language="fsharp" source="./snippets/System/Object/GetHashCode/fsharp/xor1.fs" id="Snippet2"::: :::code language="vb" source="./snippets/System/Object/GetHashCode/vb/xor1.vb" id="Snippet2"::: -The previous example returns the same hash code for (n1, n2) and (n2, n1), and so may generate more collisions than are desirable. A number of solutions are available so that hash codes in these cases are not identical. One is to return the hash code of a `Tuple` object that reflects the order of each field. The following example shows a possible implementation that uses the class. Note, though, that the performance overhead of instantiating a `Tuple` object may significantly impact the overall performance of an application that stores large numbers of objects in hash tables. +The previous example returns the same hash code for (n1, n2) and (n2, n1), and so may generate more collisions than are desirable. The recommended solution is to use , which is available starting with .NET Core 2.1. It avoids the symmetry problem and produces a well-distributed hash code without the overhead of creating a `Tuple` object. :::code language="csharp" source="./snippets/System/Object/GetHashCode/csharp/xor2.cs" id="Snippet3"::: :::code language="fsharp" source="./snippets/System/Object/GetHashCode/fsharp/xor2.fs" id="Snippet3"::: :::code language="vb" source="./snippets/System/Object/GetHashCode/vb/xor2.vb" id="Snippet3"::: -A second alternative solution involves weighting the individual hash codes by left-shifting the hash codes of successive fields by two or more bits. Optimally, bits shifted beyond bit 31 should wrap around rather than be discarded. Since bits are discarded by the left-shift operators in both C# and Visual Basic, this requires creating a left shift-and-wrap method like the following: +Before `HashCode.Combine` was available, an alternative was to weight the individual hash codes by left-shifting the hash codes of successive fields by two or more bits. Optimally, bits shifted beyond bit 31 should wrap around rather than be discarded. Since bits are discarded by the left-shift operators in both C# and Visual Basic, this requires creating a left shift-and-wrap method like the following: :::code language="csharp" source="./snippets/System/Object/GetHashCode/csharp/shift1.cs" id="Snippet4"::: :::code language="fsharp" source="./snippets/System/Object/GetHashCode/fsharp/shift1.fs" id="Snippet4"::: diff --git a/docs/visual-basic/programming-guide/concepts/covariance-contravariance/using-variance-in-interfaces-for-generic-collections.md b/docs/visual-basic/programming-guide/concepts/covariance-contravariance/using-variance-in-interfaces-for-generic-collections.md index 5157891709bed..4f5fa3b251ad9 100644 --- a/docs/visual-basic/programming-guide/concepts/covariance-contravariance/using-variance-in-interfaces-for-generic-collections.md +++ b/docs/visual-basic/programming-guide/concepts/covariance-contravariance/using-variance-in-interfaces-for-generic-collections.md @@ -82,11 +82,7 @@ Class PersonComparer Implements IEqualityComparer(Of Person).GetHashCode If person Is Nothing Then Return 0 - Dim hashFirstName = - If(person.FirstName Is Nothing, - 0, person.FirstName.GetHashCode()) - Dim hashLastName = person.LastName.GetHashCode() - Return hashFirstName Xor hashLastName + Return HashCode.Combine(person.FirstName, person.LastName) End Function End Class From dc8d44161ca8eb8f2354ac6c41b3954d877aa3b6 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 10 Mar 2026 11:18:48 -0400 Subject: [PATCH 3/7] Update docs/fundamentals/runtime-libraries/system-object-gethashcode.md --- .../fundamentals/runtime-libraries/system-object-gethashcode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fundamentals/runtime-libraries/system-object-gethashcode.md b/docs/fundamentals/runtime-libraries/system-object-gethashcode.md index 5e28c290748c3..dd40aa508feb5 100644 --- a/docs/fundamentals/runtime-libraries/system-object-gethashcode.md +++ b/docs/fundamentals/runtime-libraries/system-object-gethashcode.md @@ -58,7 +58,7 @@ Frequently, a type has multiple data fields that can participate in generating t :::code language="fsharp" source="./snippets/System/Object/GetHashCode/fsharp/xor1.fs" id="Snippet2"::: :::code language="vb" source="./snippets/System/Object/GetHashCode/vb/xor1.vb" id="Snippet2"::: -The previous example returns the same hash code for (n1, n2) and (n2, n1), and so may generate more collisions than are desirable. The recommended solution is to use , which is available starting with .NET Core 2.1. It avoids the symmetry problem and produces a well-distributed hash code without the overhead of creating a `Tuple` object. +The previous example returns the same hash code for (n1, n2) and (n2, n1), and so may generate more collisions than are desirable. The recommended solution is to use . It avoids the symmetry problem and produces a well-distributed hash code without the overhead of creating a `Tuple` object. :::code language="csharp" source="./snippets/System/Object/GetHashCode/csharp/xor2.cs" id="Snippet3"::: :::code language="fsharp" source="./snippets/System/Object/GetHashCode/fsharp/xor2.fs" id="Snippet3"::: From 8952a285b553ce5ce6766b83c0d8f59b874f8851 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 10 Mar 2026 11:18:55 -0400 Subject: [PATCH 4/7] Update docs/fundamentals/runtime-libraries/system-object-gethashcode.md --- .../fundamentals/runtime-libraries/system-object-gethashcode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fundamentals/runtime-libraries/system-object-gethashcode.md b/docs/fundamentals/runtime-libraries/system-object-gethashcode.md index dd40aa508feb5..94183e4b23c73 100644 --- a/docs/fundamentals/runtime-libraries/system-object-gethashcode.md +++ b/docs/fundamentals/runtime-libraries/system-object-gethashcode.md @@ -64,7 +64,7 @@ The previous example returns the same hash code for (n1, n2) and (n2, n1), and s :::code language="fsharp" source="./snippets/System/Object/GetHashCode/fsharp/xor2.fs" id="Snippet3"::: :::code language="vb" source="./snippets/System/Object/GetHashCode/vb/xor2.vb" id="Snippet3"::: -Before `HashCode.Combine` was available, an alternative was to weight the individual hash codes by left-shifting the hash codes of successive fields by two or more bits. Optimally, bits shifted beyond bit 31 should wrap around rather than be discarded. Since bits are discarded by the left-shift operators in both C# and Visual Basic, this requires creating a left shift-and-wrap method like the following: +In .NET Framework, an alternative is to weight the individual hash codes by left-shifting the hash codes of successive fields by two or more bits. Optimally, bits shifted beyond bit 31 should wrap around rather than be discarded. Since bits are discarded by the left-shift operators in both C# and Visual Basic, this requires creating a left shift-and-wrap method like the following: :::code language="csharp" source="./snippets/System/Object/GetHashCode/csharp/shift1.cs" id="Snippet4"::: :::code language="fsharp" source="./snippets/System/Object/GetHashCode/fsharp/shift1.fs" id="Snippet4"::: From f13263e00b6aadb369bf14c62570e5537ccd6dd6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 15:32:13 +0000 Subject: [PATCH 5/7] Add missing fsproj for F# Equals snippets to fix CI build Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- .../Object/Equals/fsharp/Project.fsproj | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/fsharp/Project.fsproj diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/fsharp/Project.fsproj b/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/fsharp/Project.fsproj new file mode 100644 index 0000000000000..a045093666eef --- /dev/null +++ b/docs/fundamentals/runtime-libraries/snippets/System/Object/Equals/fsharp/Project.fsproj @@ -0,0 +1,19 @@ + + + + Library + net10.0 + + + + + + + + + + + + + + From 67ad36892bbc449661505ed87986ce3a207fe108 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:03:54 +0000 Subject: [PATCH 6/7] Add missing fsproj for F# GetHashCode snippets to fix CI build Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- .../Object/GetHashCode/fsharp/Project.fsproj | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/fsharp/Project.fsproj diff --git a/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/fsharp/Project.fsproj b/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/fsharp/Project.fsproj new file mode 100644 index 0000000000000..77f3e54624c69 --- /dev/null +++ b/docs/fundamentals/runtime-libraries/snippets/System/Object/GetHashCode/fsharp/Project.fsproj @@ -0,0 +1,15 @@ + + + + Library + net10.0 + + + + + + + + + + From 34bcb6c987024b12f95bc708448e221b8849ceb7 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 10 Mar 2026 14:27:13 -0400 Subject: [PATCH 7/7] Apply suggestion from @BillWagner --- .../fundamentals/runtime-libraries/system-object-gethashcode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/fundamentals/runtime-libraries/system-object-gethashcode.md b/docs/fundamentals/runtime-libraries/system-object-gethashcode.md index 94183e4b23c73..f156c0bfcd0b5 100644 --- a/docs/fundamentals/runtime-libraries/system-object-gethashcode.md +++ b/docs/fundamentals/runtime-libraries/system-object-gethashcode.md @@ -58,7 +58,7 @@ Frequently, a type has multiple data fields that can participate in generating t :::code language="fsharp" source="./snippets/System/Object/GetHashCode/fsharp/xor1.fs" id="Snippet2"::: :::code language="vb" source="./snippets/System/Object/GetHashCode/vb/xor1.vb" id="Snippet2"::: -The previous example returns the same hash code for (n1, n2) and (n2, n1), and so may generate more collisions than are desirable. The recommended solution is to use . It avoids the symmetry problem and produces a well-distributed hash code without the overhead of creating a `Tuple` object. +The previous example returns the same hash code for (n1, n2) and (n2, n1), and so may generate more collisions than are desirable. On .NET 5+, the recommended solution is to use . It avoids the symmetry problem and produces a well-distributed hash code without the overhead of creating a `Tuple` object. :::code language="csharp" source="./snippets/System/Object/GetHashCode/csharp/xor2.cs" id="Snippet3"::: :::code language="fsharp" source="./snippets/System/Object/GetHashCode/fsharp/xor2.fs" id="Snippet3":::