CA1036: 比較可能な型でメソッドをオーバーライドします

プロパティ
ルール ID CA1036
Title 比較可能な型でメソッドをオーバーライドします
[カテゴリ] デザイン
修正が中断ありか中断なしか なし
.NET 8 では既定で有効 いいえ

原因

型に System.IComparable インターフェイスが実装され、System.Object.Equals がオーバーライドされていないか、等式、不等式、小なり、または大なりの言語固有の演算子がオーバーロードされていません。 型によってインターフェイスの実装のみが継承されている場合、この規則によって違反は報告されません。

既定では、この規則の対象は外部から参照できる型のみですが、これは構成可能です。

規則の説明

カスタムの並べ替え順序を定義する型には IComparable インターフェイスが実装されています。 CompareTo メソッドからは、その型の 2 つのインスタンスの正しい並べ替え順序を示す整数値が返されます。 この規則により、並べ替え順序を設定する型が識別されます。 並べ替え順序を設定すると、等式、不等式、小なり、大なりの通常の意味が適用されなくなります。 IComparable の実装を提供する場合、通常は Equals もオーバーライドして CompareTo と一致する値を返すようにする必要があります。 Equals をオーバーライドし、演算子のオーバーロードをサポートする言語でコーディングしている場合は、Equals と一致する演算子も指定する必要があります。

違反の修正方法

この規則の違反を修正するには、Equals をオーバーライドします。 演算子のオーバーロードをサポートするプログラミング言語の場合は、次の演算子を指定します。

  • op_Equality
  • op_Inequality
  • op_LessThan
  • op_GreaterThan

C# では、これらの演算子を表すために使用されるトークンは次のとおりです。

==
!=
<
>

どのようなときに警告を抑制するか

Visual Basic の場合のように、違反の原因が演算子の欠落であり、演算子のオーバーロードをサポートしていないプログラミング言語の場合は、規則 CA1036 からの警告を表示しないようにしても問題ありません。 アプリのコンテキストでは演算子を実装しても意味がないと判断した場合は、op_Equality 以外の等価演算子で発生したときに、この規則からの警告を表示しないようにしても問題ありません。 ただし、Object.Equals をオーバーライドする場合は、常に op_Equality と == 演算子をオーバーライドする必要があります。

警告を抑制する

単一の違反を抑制するだけの場合は、ソース ファイルにプリプロセッサ ディレクティブを追加して無効にしてから、規則をもう一度有効にします。

#pragma warning disable CA1036
// The code that's violating the rule is on this line.
#pragma warning restore CA1036

ファイル、フォルダー、またはプロジェクトの規則を無効にするには、構成ファイルでその重要度を none に設定します。

[*.{cs,vb}]
dotnet_diagnostic.CA1036.severity = none

詳細については、「コード分析の警告を抑制する方法」を参照してください。

分析するコードを構成する

次のオプションを使用して、コードベースのどの部分に対してこの規則を実行するか構成します。

このオプションを構成できる対象は、この規則だけ、それを適用するすべての規則、それを適用するこのカテゴリ (デザイン) のすべての規則のいずれかです。 詳細については、「コード品質規則の構成オプション」を参照してください。

特定の API サーフェイスを含める

ユーザー補助に基づいて、この規則を実行するコードベースの部分を構成できます。 たとえば、非パブリック API サーフェイスでのみ規則を実行するように指定するには、プロジェクトの .editorconfig ファイルに次のキーと値のペアを追加します。

dotnet_code_quality.CAXXXX.api_surface = private, internal

使用例

次のコードには、IComparable を正しく実装する型が含まれています。 コードのコメントで、Equals および IComparable インターフェイスに関連するさまざまな規則を満たすメソッドを識別しています。

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

        Rating = v;
    }

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

    public int CompareTo(RatingInformation? other)
    {
        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);
    }

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

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

        return false;
    }

    // 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 (left is null)
        {
            return right is 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 の実装の動作をテストするものです。

public class Test
{
    public static void Main1036(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);
    }
}

関連項目