CA1036: Invalidar métodos en tipos comparables

Propiedad Value
Identificador de la regla CA1036
Título Invalidar métodos en tipos comparables
Categoría Diseño
La corrección es problemática o no problemática Poco problemático
Habilitado de forma predeterminada en .NET 8 No

Causa

Un tipo implementa la interfaz System.IComparable y no invalida System.Object.Equals o no sobrecarga el operador específico del lenguaje con igualdad, desigualdad, menor que o mayor que. La regla no notifica una infracción si el tipo hereda solo una implementación de la interfaz.

De forma predeterminada, esta regla solo examina los tipos visibles externamente, pero es configurable.

Descripción de la regla

Los tipos que definen un criterio de ordenación personalizado implementan la interfaz IComparable. El método CompareTo devuelve un valor entero que indica el criterio de ordenación correcto para dos instancias del tipo. Esta regla identifica los tipos que establecen un criterio de ordenación. Establecer un criterio de ordenación implica que no se aplica el significado habitual de igualdad, desigualdad, menor que y mayor que. Cuando se proporciona una implementación de IComparable, normalmente también se debe invalidar Equals para que devuelva valores que sean coherentes con CompareTo. Si invalida Equals y está codificando en un lenguaje que admite sobrecargas de operador, también debe proporcionar operadores coherentes con Equals.

Cómo corregir infracciones

Para corregir una infracción de esta regla, invalide Equals. Si el lenguaje de programación admite la sobrecarga de operador, proporcione los operadores siguientes:

  • op_Equality
  • op_Inequality
  • op_LessThan
  • op_GreaterThan

En C#, los tokens que se usan para representar estos operadores son los siguientes:

==
!=
<
>

Cuándo suprimir las advertencias

Es seguro suprimir una advertencia de la regla CA1036 cuando la infracción está causada por la ausencia de operadores y el lenguaje de programación no admite la sobrecarga de operador, como es el caso de Visual Basic. Si determina que la implementación de los operadores no tiene sentido en el contexto de la aplicación, también es seguro suprimir una advertencia de esta regla cuando se activa en operadores de igualdad distintos de op_Equality. Sin embargo, siempre debe invalidar op_Equality y el operador = = si invalida Object.Equals.

Supresión de una advertencia

Si solo quiere suprimir una única infracción, agregue directivas de preprocesador al archivo de origen para deshabilitar y volver a habilitar la regla.

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

Para deshabilitar la regla de un archivo, una carpeta o un proyecto, establezca su gravedad en none del archivo de configuración.

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

Para obtener más información, consulte Procedimiento para suprimir advertencias de análisis de código.

Configuración del código para analizar

Use la opción siguiente para configurar en qué partes del código base ejecutar esta regla.

Puede configurar esta opción solo para esta regla, para todas las reglas a las que se aplica o para todas las reglas de esta categoría (Diseño) a las que se aplica. Para más información, vea Opciones de configuración de reglas de calidad de código.

Incluir superficies de API específicas

Puede configurar en qué partes del código base ejecutar esta regla, en función de su accesibilidad. Por ejemplo, para especificar que la regla solo se debe ejecutar en la superficie de API no públicas, agregue el siguiente par clave-valor a un archivo .editorconfig en el proyecto:

dotnet_code_quality.CAXXXX.api_surface = private, internal

Ejemplos

El siguiente código contiene un tipo que implementa IComparable correctamente. Los comentarios de código identifican los métodos que cumplen varias reglas que están relacionadas con Equals y la interfaz 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);
    }
}

El siguiente código de aplicación comprueba el comportamiento de la implementación IComparable que se mostró anteriormente.

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

Consulte también