Override equals and operator equals on value types
For value types, the inherited Equals() uses Reflection (expensive) and compares all instance fields (often unnecessary). If instances get compared, sorted or used as hash table keys, implement Equals(), GetHashCode() and the equality/inequality operators.
How to fix
Implement Equals(), GetHashCode() and the equality and inequality operators, or transform the type into a record struct.
Estimated Debt: 15 minutes plus 2 minutes per instance field.
Estimated Debt: 15 minutes plus 2 minutes per instance field.
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Override equals and operator equals on value types</Name>
// <Id>ND1313:OverrideEqualsAndOperatorEqualsOnValueTypes</Id>
warnif count > 0
from t in JustMyCode.Types where
t.IsStructure &&
// Don't warn about C# 15 union types, their struct is generated by the compiler
!t.IsUnion &&
t.InstanceFields.Count() > 0
let equalsMethod = t.InstanceMethods.Where(m0 => m0.Name == "Equals(Object)").SingleOrDefault()
where equalsMethod == null
where
// Don't warn about structure with a fixed field that is not meant to be compared
!t.Fields.Any(f => f.HasAttribute("System.Runtime.CompilerServices.FixedBufferAttribute".AllowNoMatch())) &&
// Don't warn about InlineArrayAttribute's field
!t.HasAttribute("System.Runtime.CompilerServices.InlineArrayAttribute".AllowNoMatch())
select new {
t,
t.InstanceFields,
Debt = (12 + 2*t.InstanceFields.Count()).ToMinutes().ToDebt(),
Severity = Severity.High
}
//<Expl>
// The *{0}* should override the *Equals()* and *GetHashCode()* methods.
// Additionally, consider implementing the *==* and *!=* operators
// if you anticipate users comparing or sorting instances or using
// them as keys in hash tables.
// Alternatively transform it into a *record struct*.
//</Expl>
//<Desc>
// For value types, the inherited *Equals()* uses *Reflection* (expensive)
// and compares all instance fields (often unnecessary). If instances get
// compared, sorted or used as hash table keys, implement *Equals()*,
// *GetHashCode()* and the equality/inequality operators.
//</Desc>
//<HowToFix>
// Implement *Equals()*, *GetHashCode()* and the equality and inequality
// operators, or transform the type into a *record struct*.
//
// Estimated Debt: 15 minutes plus 2 minutes per instance field.
//</HowToFix>
Open in the interactive Rules Explorer · All default rules
Design: all 16 items
- Avoid custom delegates ND1300
- Types with disposable instance fields must be disposable ND1301
- Disposable types with unmanaged resources should declare finalizer ND1302
- Methods that create disposable object(s) and that don't call Dispose() ND1303
- Disposable Types Must Unsubscribe Events ND1315
- Classes that are candidate to be turned into structures ND1304
- Avoid namespaces with few types ND1305
- Nested types should not be visible ND1306
- Declare types in namespaces ND1307
- Empty static constructor can be discarded ND1308
- Instances size shouldn't be too big ND1309
- Attribute classes should be sealed ND1310
- Don't use obsolete types, methods or fields ND1311
- Do implement methods that throw NotImplementedException ND1312
- Override equals and operator equals on value types ND1313
- Boxing/unboxing should be avoided ND1314