Override methods on comparable types

TypeName

OverrideMethodsOnComparableTypes

CheckId

CA1036

Category

Microsoft.Design

Breaking Change

NonBreaking

Cause

A public or protected type implements the System.IComparable interface and does not override System.Object.Equals or does not overload the language-specific operator for equality, inequality, less than, or greater than. The rule does not report a violation if the type only inherits an implementation of the interface.

Rule Description

Types that define a custom sort order implement the IComparable interface. The CompareTo method returns an integer value that indicates the proper sort order for two instances of the type. This rule identifies types that set a sort order, implying that the ordinary meaning of equality, inequality, less than, and greater than do not apply. When you provide an implementation of IComparable, it is usually necessary to also override Equals so that it returns values that are consistent with CompareTo. If you override Equals and are coding in a language that supports operator overloads, you should also provide operators that are consistent with Equals.

How to Fix Violations

To fix a violation of this rule, override Equals. If your programming language supports operator overloading, supply the following operators:

  • op_Equality

  • op_Inequality

  • op_LessThan

  • op_GreaterThan

In C#, the tokens used to represent these operators are: ==, !=, <, and >.

When to Exclude Warnings

It is safe to exclude a warning from this rule when the violation is caused by missing operators and your programming language does not support operator overloading, as is the case with Visual Basic .NET.

Example

The following example contains a type that correctly implements IComparable. Code comments identify the methods that satisfy various rules related to Equals and the IComparable interface.

using System;
using System.Globalization;

namespace DesignLibrary
{
   // 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 
   {
      private string rating;

      public RatingInformation (string s)
      {
         string v = s.ToUpper(CultureInfo.InvariantCulture);
         if (v.CompareTo("C") > 0 || v.CompareTo("A") < 0 || v.Length != 1)
         {
            throw new ArgumentException("Invalid rating value was specified.");
         }
         rating = v;
      }

      public int CompareTo ( object obj)
      {
         if (!(obj is RatingInformation))
         {
            throw new ArgumentException(
               "A RatingInformation object is required for comparison.");
         }
         // Ratings compare opposite to normal string order,
         // so reverse the value returned by String.CompareTo.
         return -1 * this.rating.CompareTo(((RatingInformation)obj).rating);
      }

      public string Rating 
      {
         get { return rating;}
      }

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

      // Omitting getHashCode violates rule: OverrideGetHashCodeOnOverridingEquals.
      public override int GetHashCode ()
      {
         char [] c = this.Rating.ToCharArray();
         return (int) c[0];
      }  
      // Omitting any of the following operator overloads 
      // violates rule: OverrideMethodsOnComparableTypes.
      public static bool operator == (RatingInformation r1, RatingInformation r2)
      {
         return r1.Equals(r2);
      }  
      public static bool operator != (RatingInformation r1, RatingInformation r2)
      {
        return !(r1==r2);
      }  
      public static bool operator < (RatingInformation r1, RatingInformation r2)
      {
         return (r1.CompareTo(r2) < 0);
      }  
      public static bool operator > (RatingInformation r1, RatingInformation r2)
      {
         return (r1.CompareTo(r2) > 0);
      }  
   }
}

The following application tests the behavior of the IComparable implementation shown earlier.

using System;

namespace DesignLibrary
{
    public class Test
    {
       public static void Main(string [] args)
       {
          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);      
       }
    }
}

See Also

Reference

Guidelines for Implementing Equals and the Equality Operator (==)
System.IComparable
System.Object.Equals