CA1036: Substituir métodos em tipos comparáveis

Property Valor
ID da regra CA1036
Título Substituir métodos em tipos comparáveis
Categoria Projetar
Correção interruptiva ou sem interrupção Sem interrupção
Habilitado por padrão no .NET 8 Não

Causa

Um tipo implementa a interface System.IComparable e não substitui System.Object.Equals nem sobrecarrega o operador específico da linguagem para igualdade, desigualdade, menor que ou maior que. A regra não relatará uma violação se o tipo herdar apenas uma implementação da interface.

Por padrão, essa regra apenas analisa os tipos visíveis externamente, mas isso é configurável.

Descrição da regra

Tipos que definem uma ordem de classificação personalizada implementam a interface IComparable. O método CompareTo retorna um valor inteiro que indica a ordem de classificação correta para duas instâncias do tipo. Essa regra identifica tipos que definem uma ordem de classificação. Definir uma ordem de classificação implica que o significado comum de igualdade, desigualdade, menor que e maior que não se aplica. Quando você fornece uma implementação de IComparable, geralmente, você também deve substituir Equals para que ele retorne valores consistentes com CompareTo. Se você substituir Equals e estiver codificando em uma linguagem que dá suporte a sobrecargas de operador, você também deverá fornecer operadores consistentes com Equals.

Como corrigir violações

Para corrigir uma violação desta regra, substitua Equals. Se sua linguagem de programação der suporte à sobrecarga do operador, forneça os seguintes operadores:

  • op_Equality
  • op_Inequality
  • op_LessThan
  • op_GreaterThan

No C#, os tokens usados para representar esses operadores são os seguintes:

==
!=
<
>

Quando suprimir avisos

É seguro suprimir um aviso da regra CA1036 quando a violação é causada por operadores ausentes e sua linguagem de programação não der suporte à sobrecarga do operador, como é o caso do Visual Basic. Se você determinar que a implementação dos operadores não faz sentido no contexto do aplicativo, também é seguro suprimir um aviso dessa regra quando ela é disparada em operadores de igualdade diferentes de op_Equality. No entanto, você sempre deverá substituir op_Equality e o operador == se substituir Object.Equals.

Suprimir um aviso

Para suprimir apenas uma violação, adicione diretivas de pré-processador ao arquivo de origem a fim de desabilitar e, em seguida, reabilitar a regra.

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

Para desabilitar a regra em um arquivo, uma pasta ou um projeto, defina a severidade como none no arquivo de configuração.

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

Para obter mais informações, confira Como suprimir avisos de análise de código.

Configurar código para analisar

Use a opção a seguir para configurar em quais partes da base de código essa regra deve ser executada.

Você pode configurar essa opção apenas para essa regra, para todas as regras às quais ela se aplica ou para todas as regras nessa categoria (Design) às quais ela se aplica. Para saber mais, confira Opções de configuração de regra de qualidade de código.

Incluir superfícies de API específicas

É possível configurar em quais partes da base de código essa regra deverá ser executada, com base na acessibilidade. Por exemplo, para especificar que a regra deverá ser executada apenas na superfície de API não pública, adicione o seguinte par chave-valor a um arquivo .editorconfig no projeto:

dotnet_code_quality.CAXXXX.api_surface = private, internal

Exemplos

O código a seguir contém um tipo que implementa IComparable corretamente. Os comentários de código identificam os métodos que atendem a várias regras relacionadas às interfaces Equals e 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);
    }
}

O código do aplicativo a seguir testa o comportamento da implementação IComparable que foi mostrada 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);
    }
}

Confira também