CA1036 : Substituer les méthodes sur les types Comparable

Propriété Value
Identificateur de la règle CA1036
Titre Substituer les méthodes sur les types Comparable
Catégorie Conception
Le correctif est cassant ou non cassant Sans rupture
Activé par défaut dans .NET 8 Non

Cause

Un type implémente l’interface System.IComparable et ne remplace pas System.Object.Equals ou ne surcharge pas l’opérateur spécifique au langage pour l’égalité, l’inégalité, les valeurs inférieures ou supérieures. La règle ne signale pas de violation si le type hérite uniquement d’une implémentation de l’interface.

Par défaut, cette règle examine uniquement les types visibles en externe, mais elle est configurable.

Description de la règle

Les types qui définissent un ordre de tri personnalisé implémentent l’interface IComparable. La méthode CompareTo retourne une valeur entière qui indique l’ordre de tri correct pour deux instances du type. Cette règle identifie les types qui définissent un ordre de tri. La définition d’un ordre de tri implique que la signification ordinaire de l’égalité, de l’inégalité, des valeurs inférieures et supérieures ne s’applique pas. Lorsque vous fournissez une implémentation de IComparable, vous devez généralement également remplacer Equals afin qu’il retourne des valeurs cohérentes avec CompareTo. Si vous remplacez Equals et codez dans un langage qui prend en charge les surcharges d’opérateur, vous devez également fournir des opérateurs cohérents avec Equals.

Comment corriger les violations

Pour corriger toute violation de cette règle, remplacez Equals. Si votre langage de programmation prend en charge la surcharge des opérateurs, fournissez les opérateurs suivants :

  • op_Equality
  • op_Inequality
  • op_LessThan
  • op_GreaterThan

En C#, les jetons utilisés pour représenter ces opérateurs sont les suivants :

==
!=
<
>

Quand supprimer les avertissements

Il est possible de supprimer un avertissement de la règle CA1036 quand la violation est due à des opérateurs manquants et que votre langage de programmation ne prend pas en charge la surcharge des opérateurs, comme c’est le cas avec Visual Basic. Si vous déterminez que l’implémentation des opérateurs n’a pas de sens dans le contexte de votre application, il est également prudent de supprimer un avertissement de cette règle lorsqu’elle se déclenche sur des opérateurs d’égalité autres que op_Equality. Toutefois, vous devez toujours remplacer op_Equality et l’opérateur == si vous remplacez Object.Equals.

Supprimer un avertissement

Si vous voulez supprimer une seule violation, ajoutez des directives de préprocesseur à votre fichier source pour désactiver et réactiver la règle.

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

Pour désactiver la règle sur un fichier, un dossier ou un projet, définissez sa gravité sur none dans le fichier de configuration.

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

Pour plus d’informations, consultez Comment supprimer les avertissements de l’analyse de code.

Configurer le code à analyser

Utilisez l’option suivante pour configurer les parties de votre codebase sur lesquelles exécuter cette règle.

Vous pouvez configurer cette option pour cette règle uniquement, pour toutes les règles auxquelles elle s’applique ou pour toutes les règles de cette catégorie (Conception) auxquelles elle s’applique. Pour plus d’informations, consultez Options de configuration des règles de qualité du code.

Inclure des surfaces d’API spécifiques

Vous pouvez configurer les parties de votre codebase sur lesquelles exécuter cette règle, en fonction de leur accessibilité. Par exemple, pour spécifier que la règle doit s’exécuter uniquement sur la surface d’API non publique, ajoutez la paire clé-valeur suivante à un fichier .editorconfig dans votre projet :

dotnet_code_quality.CAXXXX.api_surface = private, internal

Exemples

Le code suivant contient un type qui implémente IComparable correctement. Les commentaires de code identifient les méthodes qui répondent à différentes règles liées à l’interface Equals et à l’interface 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);
    }
}

Le code d’application suivant teste le comportement de l’implémentation IComparable qui a été illustrée précédemment.

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

Voir aussi