Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions docs/fundamentals/code-analysis/quality-rules/ca1036.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ helpviewer_keywords:
- CA1036
author: gewarren
ms.author: gewarren
dev_langs:
- CSharp
- VB
---
# CA1036: Override methods on comparable types

Expand All @@ -35,23 +38,42 @@ Types that define a custom sort order implement the <xref:System.IComparable> in

To fix a violation of this rule, override <xref:System.Object.Equals%2A>. If your programming language supports operator overloading, supply the following operators:

- op_Equality
- op_Inequality
- op_LessThan
- op_GreaterThan

In C#, the tokens that are used to represent these operators are as follows:
- `op_Equality`
- `op_Inequality`
- `op_LessThan`
- `op_GreaterThan`

```csharp
==
!=
<
>
// In C#, implement these operators.
public static bool operator ==(SampleClass? one, SampleClass? other) { }
public static bool operator !=(SampleClass? one, SampleClass? other) { }
public static bool operator <(SampleClass? one, SampleClass? other) { }
public static bool operator >(SampleClass? one, SampleClass? other) { }
```

```vb
' In Visual Basic, implement these operators.

Public Shared Operator =(one As SampleClass, other As SampleClass) As Boolean
...
End Operator

Public Shared Operator <>(one As SampleClass, other As SampleClass) As Boolean
...
End Operator

Public Shared Operator <(one As SampleClass, other As SampleClass) As Boolean
...
End Operator

Public Shared Operator >(one As SampleClass, other As SampleClass) As Boolean
...
End Operator
```

## When to suppress warnings

It is safe to suppress a warning from rule CA1036 when the violation is caused by missing operators and your programming language does not support operator overloading, as is the case with Visual Basic. If you determine that implementing the operators does not make sense in your app context, it's also safe to suppress a warning from this rule when it fires on equality operators other than op_Equality. However, you should always override op_Equality and the == operator if you override <xref:System.Object.Equals%2A?displayProperty=nameWithType>.
It's safe to suppress a warning from rule CA1036 when the violation is caused by missing operators and your programming language does not support operator overloading. If you determine that implementing the operators does not make sense in your app context, it's also safe to suppress a warning from this rule when it fires on equality operators other than `op_Equality`. However, you should always override `op_Equality` and the `==` operator if you override <xref:System.Object.Equals%2A?displayProperty=nameWithType>.

## Suppress a warning

Expand Down Expand Up @@ -87,10 +109,12 @@ You can configure this option for just this rule, for all rules it applies to, o
The following code contains a type that correctly implements <xref:System.IComparable>. Code comments identify the methods that satisfy various rules that are related to <xref:System.Object.Equals%2A> and the <xref:System.IComparable> interface.

:::code language="csharp" source="snippets/csharp/all-rules/ca1036.cs" id="snippet1":::
:::code language="vb" source="snippets/vb/all-rules/ca1036.vb" id="snippet1":::

The following application code tests the behavior of the <xref:System.IComparable> implementation that was shown earlier.

:::code language="csharp" source="snippets/csharp/all-rules/ca1036.cs" id="snippet2":::
:::code language="vb" source="snippets/vb/all-rules/ca1036.vb" id="snippet2":::

## See also

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TestCompare.Main1036("A", "B");
Original file line number Diff line number Diff line change
@@ -1,143 +1,134 @@
using System;
using System.Globalization;

namespace ca1036
//<snippet1>
// Valid ratings are between A and C.
// A is the highest rating; it is greater than any other valid rating.
// C is the lowest rating; it is less than any other valid rating.

public class RatingInformation : IComparable, IComparable<RatingInformation>
{
//<snippet1>
// Valid ratings are between A and C.
// A is the highest rating; it is greater than any other valid rating.
// C is the lowest rating; it is less than any other valid rating.
public string Rating { get; private set; }

public class RatingInformation : IComparable, IComparable<RatingInformation>
public RatingInformation(string rating)
{
public string Rating
{
get;
private set;
}
ArgumentNullException.ThrowIfNull(rating);

public RatingInformation(string rating)
string v = rating.ToUpper(CultureInfo.InvariantCulture);
if (v.Length != 1 ||
string.Compare(v, "C", StringComparison.Ordinal) > 0 ||
string.Compare(v, "A", StringComparison.Ordinal) < 0)
{
if (rating == null)
{
throw new ArgumentNullException("rating");
}

string v = rating.ToUpper(CultureInfo.InvariantCulture);
if (v.Length != 1 || string.Compare(v, "C", StringComparison.Ordinal) > 0 || string.Compare(v, "A", StringComparison.Ordinal) < 0)
{
throw new ArgumentException("Invalid rating value was specified.", "rating");
}

Rating = v;
throw new ArgumentException("Invalid rating value was specified.", nameof(rating));
}

public int CompareTo(object? obj)
{
if (obj == null)
{
return 1;
}

if (obj is RatingInformation other)
{
return CompareTo(other);
}

throw new ArgumentException("A RatingInformation object is required for comparison.", "obj");
}
Rating = v;
}

public int CompareTo(RatingInformation? other)
public int CompareTo(object? obj)
{
if (obj == null)
{
if (other is null)
{
return 1;
}

// Ratings compare opposite to normal string order,
// so reverse the value returned by String.CompareTo.
return -string.Compare(this.Rating, other.Rating, StringComparison.OrdinalIgnoreCase);
return 1;
}

public static int Compare(RatingInformation left, RatingInformation right)
if (obj is RatingInformation other)
{
if (object.ReferenceEquals(left, right))
{
return 0;
}
if (left is null)
{
return -1;
}
return left.CompareTo(right);
return CompareTo(other);
}

// Omitting Equals violates rule: OverrideMethodsOnComparableTypes.
public override bool Equals(object? obj)
{
if (obj is RatingInformation other)
{
return this.CompareTo(other) == 0;
}

return false;
}
throw new ArgumentException("A RatingInformation object is required for comparison.", nameof(obj));
}

// Omitting getHashCode violates rule: OverrideGetHashCodeOnOverridingEquals.
public override int GetHashCode()
public int CompareTo(RatingInformation? other)
{
if (other is null)
{
char[] c = this.Rating.ToCharArray();
return (int)c[0];
return 1;
}

// Omitting any of the following operator overloads
// violates rule: OverrideMethodsOnComparableTypes.
public static bool operator ==(RatingInformation left, RatingInformation right)
// Ratings compare opposite to normal string order,
// so reverse the value returned by String.CompareTo.
return -string.Compare(Rating, other.Rating, StringComparison.OrdinalIgnoreCase);
}

public static int Compare(RatingInformation left, RatingInformation right)
{
if (object.ReferenceEquals(left, right))
{
if (left is null)
{
return right is null;
}
return left.Equals(right);
return 0;
}
public static bool operator !=(RatingInformation left, RatingInformation right)
if (left is null)
{
return !(left == right);
return -1;
}
public static bool operator <(RatingInformation left, RatingInformation right)
return left.CompareTo(right);
}

// Omitting Equals violates rule: OverrideMethodsOnComparableTypes.
public override bool Equals(object? obj)
{
if (obj is RatingInformation other)
{
return (Compare(left, right) < 0);
return CompareTo(other) == 0;
}
public static bool operator >(RatingInformation left, RatingInformation right)

return false;
}

// Omitting getHashCode violates rule: OverrideGetHashCodeOnOverridingEquals.
public override int GetHashCode()
{
char[] c = Rating.ToCharArray();
return (int)c[0];
}

// Omitting any of the following operator overloads
// violates rule: OverrideMethodsOnComparableTypes.
public static bool operator ==(RatingInformation left, RatingInformation right)
{
if (left is null)
{
return (Compare(left, right) > 0);
return right is null;
}
return left.Equals(right);
}
//</snippet1>
public static bool operator !=(RatingInformation left, RatingInformation right)
{
return !(left == right);
}
public static bool operator <(RatingInformation left, RatingInformation right)
{
return (Compare(left, right) < 0);
}
public static bool operator >(RatingInformation left, RatingInformation right)
{
return (Compare(left, right) > 0);
}
}
//</snippet1>

//<snippet2>
public class Test
//<snippet2>
public class TestCompare
{
public static void Main1036(params string[] args)
{
public static void Main1036(string[] args)
if (args.Length < 2)
{
if (args.Length < 2)
{
Console.WriteLine("usage - TestRatings string 1 string2");
return;
}
RatingInformation r1 = new RatingInformation(args[0]);
RatingInformation r2 = new RatingInformation(args[1]);
string answer;

if (r1.CompareTo(r2) > 0)
answer = "greater than";
else if (r1.CompareTo(r2) < 0)
answer = "less than";
else
answer = "equal to";

Console.WriteLine("{0} is {1} {2}", r1.Rating, answer, r2.Rating);
return;
}
RatingInformation r1 = new(args[0]);
RatingInformation r2 = new(args[1]);
string answer;

if (r1.CompareTo(r2) > 0)
answer = "greater than";
else if (r1.CompareTo(r2) < 0)
answer = "less than";
else
answer = "equal to";

Console.WriteLine("{0} is {1} {2}", r1.Rating, answer, r2.Rating);
}
//</snippet2>
}
//</snippet2>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Public Class Program
Public Shared Sub Main()
TestCompare.Main1036(New String() {"C", "B"})
End Sub
End Class
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
<OutputType>Exe</OutputType>
<RootNamespace>all_rules</RootNamespace>
<TargetFramework>net8.0</TargetFramework>
<NoWarn>$(NoWarn);SYSLIB0050</NoWarn>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Program.vb" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
<PackageReference Include="System.Security.Permissions" Version="8.0.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Namespace ca1003

Class Test

Shared Sub Main()
Shared Sub Main1003()

Dim eventRaiser As New ClassThatRaisesEvent()
Dim eventHandler As New ClassThatHandlesEvent(eventRaiser)
Expand Down
Loading
Loading