JetBrains Rider 2024.3 Help

Code inspection: Usage of default struct equality

This inspection reports usages of struct types that do not have Equals/GetHashCode overrides. Usages of such structs can negatively affect performance.

In the example below, Points dictionary uses MyPoint struct as a key. This means that usages of Points will implicitly result in calling the suboptimal default implementations of Equals/GetHashCode:

public class Test { public Dictionary<MyPoint, string> Points = new(); } public struct MyPoint { public int X, Y; // No Equals/GetHashCode overrides }

To fix this performance issue, you can generate Equals/GetHashCode implementations in your struct via quick-fixes or turn your struct declaration into record struct, which will force the C# compiler to generate good Equals/GetHashCode implementations for you.

Performance problems with default implementations of Equals/GetHashCode in structs

Since the introduction of the .NET Framework 1.0, the .NET type system has offered support for value types in C# via the struct keyword. To ensure struct types behave like values, it was important to define structural equality for such types, so that any two struct instances should be considered equivalent if the values of their corresponding fields are also equivalent. To meet this requirement, the .NET runtime supplies default implementations of the Equals/GetHashCode virtual methods for all struct types, which generally do their job.

However, there are significant issues with the default implementations of Equals/GetHashCode that the runtime provides. To work with all possible struct types, the implementations of Equals/GetHashCode involve boxing allocations (to get a unified representation of the struct values as a managed reference) and rely on the reflection mechanism to acquire field values. Moreover, the default GetHashCode implementation uses the hash code of the first non-null reference type field, potentially leading to poor hash code distribution if your struct contains multiple fields. Therefore, it can be crucial to define your own Equals/GetHashCode implementations if structural equality is checked along the critical execution paths of your application.

For more information, see Microsoft Developer Support: Performance implications of default struct equality in C#

Equality-checking APIs

This problem is only reported when the struct without Equals/GetHashCode implementations is used via APIs that actually perform equality checking under the hood.

JetBrains Rider is aware of most such API patterns in system and popular libraries. For custom APIs, you can use [DefaultEqualityUsageAttribute] from JetBrains.Annotations to catch similar issues.

Last modified: 21 August 2024