CA1036:重写可比较类型中的方法

类型名

OverrideMethodsOnComparableTypes

CheckId

CA1036

类别

Microsoft.Design

是否重大更改

非重大更改

原因

公共或受保护类型实现 System.IComparable 接口,不重写 Object.Equals,也不重载表示相等、不等、小于或大于的语言特定的运算符。 如果该类型仅继承接口的实现,则该规则不会报告冲突。

规则说明

定义自定义排序顺序的类型实现 IComparable 接口。 CompareTo 方法返回一个整数值,指示该类型的两个实例的正确排序顺序。 该值标识设置排序顺序的类型;这意味着不会应用相等、不等、小于或大于的常规含义。 提供 IComparable 的实现时,通常还必须重写 Equals,以使其返回与 CompareTo 一致的值。 如果重写 Equals 并使用支持运算符重载的语言编写代码,则还应提供与 Equals 一致的运算符。

如何解决冲突

要修复与该规则的冲突,请重写 Equals。 如果您的编程语言支持运算符重载,请提供下列运算符:

  • op_Equality

  • op_Inequality

  • op_LessThan

  • op_GreaterThan

在 C# 中,用来代表这些运算符的标记如下所示:==、!=、< 和 >。

何时禁止显示警告

如果冲突是因缺少运算符引起的,且您的编程语言不支持运算符重载(例如,在 Visual Basic .NET 中就是这样),则可以安全地禁止显示此规则发出的警告。 如果您确定实现相等运算符在应用程序上下文中没有意义,则当 op_Equality 以外相等运算符上激发此规则时,禁止此规则的显示也是安全的。 但是,如果重写 Object.Equals,应该始终重写 op_Equality 和 == 运算符。

示例

下面的示例包含正确实现 IComparable 的类型。 代码注释标识满足与 EqualsIComparable 接口相关的各种规则的方法。

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, IComparable<RatingInformation>
    {
        public string Rating
        {
            get;
            private set;
        }

        public RatingInformation(string rating)
        {
            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");
            }
            this.Rating = v;
        }

        public int CompareTo(object obj)
        {
            if (obj == null)
            {
                return 1;
            }
            RatingInformation other = obj as RatingInformation; // avoid double casting
            if (other == null)
            {
                throw new ArgumentException("A RatingInformation object is required for comparison.", "obj");
            }
            return this.CompareTo(other);
        }

        public int CompareTo(RatingInformation other)
        {
            if (object.ReferenceEquals(other, 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);
        }

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

        // Omitting Equals violates rule: OverrideMethodsOnComparableTypes.
        public override bool Equals(object obj)
        {
            RatingInformation other = obj as RatingInformation; //avoid double casting
            if (object.ReferenceEquals(other, null))
            {
                return false;
            }
            return this.CompareTo(other) == 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 left, RatingInformation right)
        {
            if (object.ReferenceEquals(left, null))
            {
                return object.ReferenceEquals(right, null);
            }
            return left.Equals(right);
        }
        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);
        }
    }
}

下面的应用程序测试上文所示的 IComparable 实现的行为。

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);      
       }
    }
}

请参见

参考

等于和相等运算符 (==) 的实现准则

System.IComparable

Object.Equals