Skip to content

Commit 0ac0071

Browse files
authored
Implement IEquatable<T> on value types overriding Equals (and enable CA1066/1077) (dotnet#63690)
1 parent 5a9e584 commit 0ac0071

File tree

111 files changed

+650
-813
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+650
-813
lines changed

eng/CodeAnalysis.src.globalconfig

+2-2
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,10 @@ dotnet_diagnostic.CA1064.severity = none
152152
dotnet_diagnostic.CA1065.severity = none
153153

154154
# CA1066: Implement IEquatable when overriding Object.Equals
155-
dotnet_diagnostic.CA1066.severity = none
155+
dotnet_diagnostic.CA1066.severity = warning
156156

157157
# CA1067: Override Object.Equals(object) when implementing IEquatable<T>
158-
dotnet_diagnostic.CA1067.severity = none
158+
dotnet_diagnostic.CA1067.severity = warning
159159

160160
# CA1068: CancellationToken parameters must come last
161161
dotnet_diagnostic.CA1068.severity = none

src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj

-2
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,6 @@
140140
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\StackFrame.CoreCLR.cs" />
141141
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\StackFrameHelper.cs" />
142142
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\StackTrace.CoreCLR.cs" />
143-
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\SymbolStore\SymAddressKind.cs" />
144-
<Compile Include="$(BclSourcesRoot)\System\Diagnostics\SymbolStore\Token.cs" />
145143
<Compile Include="$(BclSourcesRoot)\System\Enum.CoreCLR.cs" />
146144
<Compile Include="$(BclSourcesRoot)\System\Environment.CoreCLR.cs" />
147145
<Compile Include="$(BclSourcesRoot)\System\Exception.CoreCLR.cs" />

src/coreclr/System.Private.CoreLib/src/System/Diagnostics/SymbolStore/SymAddressKind.cs

-49
This file was deleted.

src/coreclr/System.Private.CoreLib/src/System/Diagnostics/SymbolStore/Token.cs

-39
This file was deleted.

src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.cs

+2
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,9 @@ public int this[int index]
194194
}
195195
}
196196

197+
#pragma warning disable CA1066 // IEquatable<MetadataImport> interface implementation isn't used
197198
internal readonly struct MetadataImport
199+
#pragma warning restore CA1067
198200
{
199201
private readonly IntPtr m_metadataImport2;
200202
private readonly object? m_keepalive;

src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1246,7 +1246,7 @@ public void GetObjectData(SerializationInfo info, StreamingContext context)
12461246
}
12471247
}
12481248

1249-
public unsafe partial struct ModuleHandle
1249+
public unsafe partial struct ModuleHandle : IEquatable<ModuleHandle>
12501250
{
12511251
#region Public Static Members
12521252
public static readonly ModuleHandle EmptyHandle;

src/coreclr/nativeaot/System.Private.CoreLib/src/System/ModuleHandle.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace System
88
{
9-
public struct ModuleHandle
9+
public struct ModuleHandle : IEquatable<ModuleHandle>
1010
{
1111
public static readonly ModuleHandle EmptyHandle;
1212

src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/General/TypeUnifier.NativeFormat.cs

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
// - The TypeUnifier extension class provides a more friendly interface to the rest of the codebase.
3535
//
3636

37+
#pragma warning disable CA1067 // override Equals because it implements IEquatable<T>
38+
3739
namespace System.Reflection.Runtime.General
3840
{
3941
internal static partial class TypeUnifier

src/coreclr/nativeaot/System.Private.Reflection.Core/src/System/Reflection/Runtime/TypeInfos/RuntimeNamedTypeInfo.cs

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
using Internal.Reflection.Tracing;
1212

13+
#pragma warning disable CA1067 // override Equals because it implements IEquatable<T>
14+
1315
namespace System.Reflection.Runtime.TypeInfos
1416
{
1517
//

src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator/ReaderGen.cs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public void EmitSource()
2020
WriteLine("#pragma warning disable 649");
2121
WriteLine("#pragma warning disable 169");
2222
WriteLine("#pragma warning disable 282 // There is no defined ordering between fields in multiple declarations of partial class or struct");
23+
WriteLine("#pragma warning disable CA1066 // IEquatable<T> implementations aren't used");
2324
WriteLine("#pragma warning disable IDE0059");
2425
WriteLine();
2526

src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeFormatReaderGen.cs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#pragma warning disable 649
77
#pragma warning disable 169
88
#pragma warning disable 282 // There is no defined ordering between fields in multiple declarations of partial class or struct
9+
#pragma warning disable CA1066 // IEquatable<T> implementations aren't used
910
#pragma warning disable IDE0059
1011

1112
using System;

src/coreclr/tools/Common/Internal/Metadata/NativeFormat/NativeMetadataReader.cs

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
54
#pragma warning disable 169
6-
7-
// There is no defined ordering between fields in multiple declarations of partial class or struct
8-
#pragma warning disable 282
9-
5+
#pragma warning disable 282 // There is no defined ordering between fields in multiple declarations of partial class or struct
6+
#pragma warning disable CA1066 // IEquatable<T> implementations aren't used
107

118
using System;
129
using System.IO;

src/coreclr/tools/Common/TypeSystem/Common/LayoutInt.cs

+2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ namespace Internal.TypeSystem
1212
/// type system do not have a known size. This type is used to make such sizes viral through the type layout
1313
/// computations)
1414
/// </summary>
15+
#pragma warning disable CA1066 // IEquatable<T> implementation wouldn't be used
1516
public struct LayoutInt
17+
#pragma warning restore CA1066
1618
{
1719
private int _value;
1820

src/libraries/Common/src/Interop/Unix/System.Native/Interop.IPAddress.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5-
using System.Diagnostics;
5+
using System.Diagnostics.CodeAnalysis;
66
using System.Runtime.InteropServices;
7-
using System.Text;
87

98
internal static partial class Interop
109
{
@@ -33,6 +32,19 @@ public bool IsIPv6
3332
private uint _isIPv6; // Non-zero if this is an IPv6 address; zero for IPv4.
3433
internal uint ScopeId; // Scope ID (IPv6 only)
3534

35+
public override unsafe int GetHashCode()
36+
{
37+
HashCode h = default;
38+
fixed (byte* ptr = Address)
39+
{
40+
h.AddBytes(new ReadOnlySpan<byte>(ptr, IsIPv6 ? IPv6AddressBytes : IPv4AddressBytes));
41+
}
42+
return h.ToHashCode();
43+
}
44+
45+
public override bool Equals([NotNullWhen(true)] object? obj) =>
46+
obj is IPAddress other && Equals(other);
47+
3648
public bool Equals(IPAddress other)
3749
{
3850
int addressByteCount;

src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceCacheKey.cs

+9-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Diagnostics.CodeAnalysis;
56

67
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
78
{
@@ -33,10 +34,14 @@ public ServiceCacheKey(Type? type, int slot)
3334
Slot = slot;
3435
}
3536

36-
public bool Equals(ServiceCacheKey other)
37-
{
38-
return Type == other.Type && Slot == other.Slot;
39-
}
37+
/// <summary>Indicates whether the current instance is equal to another instance of the same type.</summary>
38+
/// <param name="other">An instance to compare with this instance.</param>
39+
/// <returns>true if the current instance is equal to the other instance; otherwise, false.</returns>
40+
public bool Equals(ServiceCacheKey other) =>
41+
Type == other.Type && Slot == other.Slot;
42+
43+
public override bool Equals([NotNullWhen(true)] object? obj) =>
44+
obj is ServiceCacheKey other && Equals(other);
4045

4146
public override int GetHashCode()
4247
{

src/libraries/Microsoft.Extensions.Logging.Abstractions/ref/Microsoft.Extensions.Logging.Abstractions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace Microsoft.Extensions.Logging
88
{
9-
public readonly partial struct EventId
9+
public readonly partial struct EventId : System.IEquatable<Microsoft.Extensions.Logging.EventId>
1010
{
1111
private readonly object _dummy;
1212
private readonly int _dummyPrimitive;

src/libraries/Microsoft.Extensions.Logging.Abstractions/src/EventId.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using System.Diagnostics.CodeAnalysis;
56

67
namespace Microsoft.Extensions.Logging
78
{
89
/// <summary>
910
/// Identifies a logging event. The primary identifier is the "Id" property, with the "Name" property providing a short description of this type of event.
1011
/// </summary>
11-
public readonly struct EventId
12+
public readonly struct EventId : IEquatable<EventId>
1213
{
1314
/// <summary>
1415
/// Implicitly creates an EventId from the given <see cref="int"/>.

src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.HashBucket.cs

+2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ public partial class ImmutableDictionary<TKey, TValue>
1515
/// <summary>
1616
/// Contains all the key/values in the collection that hash to the same value.
1717
/// </summary>
18+
#pragma warning disable CA1066 // Implement IEquatable when overriding Object.Equals
1819
internal readonly struct HashBucket : IEnumerable<KeyValuePair<TKey, TValue>>
20+
#pragma warning restore CA1066
1921
{
2022
/// <summary>
2123
/// One of the values in this bucket.

src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.HashBucket.cs

+2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ internal enum OperationResult
3030
/// <summary>
3131
/// Contains all the keys in the collection that hash to the same value.
3232
/// </summary>
33+
#pragma warning disable CA1066 // Implement IEquatable when overriding Object.Equals
3334
internal readonly struct HashBucket
35+
#pragma warning restore CA1066
3436
{
3537
/// <summary>
3638
/// One of the values in this bucket.

src/libraries/System.Collections.Specialized/ref/System.Collections.Specialized.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
namespace System.Collections.Specialized
88
{
9-
public partial struct BitVector32
9+
public partial struct BitVector32 : System.IEquatable<System.Collections.Specialized.BitVector32>
1010
{
1111
private int _dummyPrimitive;
1212
public BitVector32(System.Collections.Specialized.BitVector32 value) { throw null; }
@@ -18,11 +18,12 @@ public partial struct BitVector32
1818
public static int CreateMask(int previous) { throw null; }
1919
public static System.Collections.Specialized.BitVector32.Section CreateSection(short maxValue) { throw null; }
2020
public static System.Collections.Specialized.BitVector32.Section CreateSection(short maxValue, System.Collections.Specialized.BitVector32.Section previous) { throw null; }
21+
public bool Equals(System.Collections.Specialized.BitVector32 other) { throw null; }
2122
public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? o) { throw null; }
2223
public override int GetHashCode() { throw null; }
2324
public override string ToString() { throw null; }
2425
public static string ToString(System.Collections.Specialized.BitVector32 value) { throw null; }
25-
public readonly partial struct Section
26+
public readonly partial struct Section : System.IEquatable<System.Collections.Specialized.BitVector32.Section>
2627
{
2728
private readonly int _dummyPrimitive;
2829
public short Mask { get { throw null; } }

src/libraries/System.Collections.Specialized/src/System/Collections/Specialized/BitVector32.cs

+8-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace System.Collections.Specialized
1010
/// <para>Provides a simple light bit vector with easy integer or Boolean access to
1111
/// a 32 bit storage.</para>
1212
/// </devdoc>
13-
public struct BitVector32
13+
public struct BitVector32 : IEquatable<BitVector32>
1414
{
1515
private uint _data;
1616

@@ -151,7 +151,12 @@ private static Section CreateSectionHelper(short maxValue, short priorMask, shor
151151
return new Section(mask, offset);
152152
}
153153

154-
public override bool Equals([NotNullWhen(true)] object? o) => o is BitVector32 other && _data == other._data;
154+
public override bool Equals([NotNullWhen(true)] object? o) => o is BitVector32 other && Equals(other);
155+
156+
/// <summary>Indicates whether the current instance is equal to another instance of the same type.</summary>
157+
/// <param name="other">An instance to compare with this instance.</param>
158+
/// <returns>true if the current instance is equal to the other instance; otherwise, false.</returns>
159+
public bool Equals(BitVector32 other) => _data == other._data;
155160

156161
public override int GetHashCode() => _data.GetHashCode();
157162

@@ -182,7 +187,7 @@ public override string ToString()
182187
/// <para>
183188
/// Represents an section of the vector that can contain a integer number.</para>
184189
/// </devdoc>
185-
public readonly struct Section
190+
public readonly struct Section : IEquatable<Section>
186191
{
187192
private readonly short _mask;
188193
private readonly short _offset;

src/libraries/System.Collections.Specialized/tests/BitVector32Tests.cs

+15
Original file line numberDiff line numberDiff line change
@@ -518,17 +518,32 @@ public static void EqualsTest()
518518
Assert.True(new BitVector32(0).Equals(original));
519519
Assert.True(original.Equals(new BitVector32(0)));
520520

521+
Assert.True(original.Equals((object)original));
522+
Assert.True(new BitVector32().Equals((object)original));
523+
Assert.True(original.Equals((object)new BitVector32()));
524+
Assert.True(new BitVector32(0).Equals((object)original));
525+
Assert.True(original.Equals((object)new BitVector32(0)));
526+
521527
BitVector32 other = new BitVector32(int.MaxValue / 2 - 1);
522528
Assert.True(other.Equals(other));
523529
Assert.True(new BitVector32(int.MaxValue / 2 - 1).Equals(other));
524530
Assert.True(other.Equals(new BitVector32(int.MaxValue / 2 - 1)));
525531

532+
Assert.True(other.Equals((object)other));
533+
Assert.True(new BitVector32(int.MaxValue / 2 - 1).Equals((object)other));
534+
Assert.True(other.Equals((object)new BitVector32(int.MaxValue / 2 - 1)));
535+
526536
Assert.False(other.Equals(original));
527537
Assert.False(original.Equals(other));
528538
Assert.False(other.Equals(null));
529539
Assert.False(original.Equals(null));
530540
Assert.False(other.Equals(int.MaxValue / 2 - 1));
531541
Assert.False(original.Equals(0));
542+
543+
Assert.False(other.Equals((object)original));
544+
Assert.False(original.Equals((object)other));
545+
Assert.False(other.Equals(int.MaxValue / 2 - 1));
546+
Assert.False(original.Equals(0));
532547
}
533548

534549
[Fact]

0 commit comments

Comments
 (0)