Udostępnij za pośrednictwem


CA1036: Przesłaniaj metody na typach porównywalnych

Właściwości Wartość
Identyfikator reguły CA1036
Tytuł Przesłaniaj metody porównywalnych typów
Kategoria Projekt
Poprawka powodująca niezgodność lub niezgodność Niezgodność
Domyślnie włączone na platformie .NET 9 Nie.

Przyczyna

Typ implementuje System.IComparable interfejs i nie System.Object.Equals zastępuje lub nie przeciąża operatora specyficznego dla języka pod kątem równości, nierówności, mniejszej niż lub większej. Reguła nie zgłasza naruszenia, jeśli typ dziedziczy tylko implementację interfejsu.

Domyślnie ta reguła analizuje tylko typy widoczne zewnętrznie, ale można to skonfigurować.

Opis reguły

Typy definiujące niestandardową kolejność sortowania IComparable implementują interfejs. Metoda CompareTo zwraca wartość całkowitą, która wskazuje prawidłową kolejność sortowania dla dwóch wystąpień typu. Ta reguła identyfikuje typy, które ustawiają kolejność sortowania. Ustawienie kolejności sortowania oznacza, że zwykłe znaczenie równości, nierówności, mniejsze niż i większe niż nie mają zastosowania. Po podaniu implementacji IComparableelementu należy zwykle również zastąpić Equals wartość , aby zwracała wartości zgodne z parametrem CompareTo. Jeśli zastąpisz Equals kodowanie i kodujesz w języku obsługującym przeciążenia operatorów, należy również udostępnić operatory zgodne z elementem Equals.

Jak naprawić naruszenia

Aby naprawić naruszenie tej reguły, zastąpij Equalswartość . Jeśli język programowania obsługuje przeciążenie operatora, podaj następujące operatory:

  • op_Equality
  • op_Inequality
  • op_LessThan
  • op_GreaterThan
// In C#, implement these operators.
public static bool operator ==(SampleClass? one, SampleClass? other) { }
public static bool operator !=(SampleClass? one, SampleClass? other) { }
public static bool operator <(SampleClass? one, SampleClass? other) { }
public static bool operator >(SampleClass? one, SampleClass? other) { }
' In Visual Basic, implement these operators.

Public Shared Operator =(one As SampleClass, other As SampleClass) As Boolean
    ...
End Operator

Public Shared Operator <>(one As SampleClass, other As SampleClass) As Boolean
    ...
End Operator

Public Shared Operator <(one As SampleClass, other As SampleClass) As Boolean
    ...
End Operator

Public Shared Operator >(one As SampleClass, other As SampleClass) As Boolean
    ...
End Operator

Kiedy pomijać ostrzeżenia

Można bezpiecznie pominąć ostrzeżenie reguły CA1036, gdy naruszenie jest spowodowane przez brakujące operatory, a język programowania nie obsługuje przeciążenia operatora. Jeśli określisz, że implementacja operatorów nie ma sensu w kontekście aplikacji, można również bezpiecznie pominąć ostrzeżenie z tej reguły, gdy jest uruchamiana na operatorach równości innych niż op_Equality. Jednak zawsze należy zastąpić op_Equality operator i == operator, jeśli zastąpisz Object.Equals.

Pomijanie ostrzeżenia

Jeśli chcesz po prostu pominąć pojedyncze naruszenie, dodaj dyrektywy preprocesora do pliku źródłowego, aby wyłączyć, a następnie ponownie włączyć regułę.

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

Aby wyłączyć regułę dla pliku, folderu lub projektu, ustaw jego ważność na none w pliku konfiguracji.

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

Aby uzyskać więcej informacji, zobacz Jak pominąć ostrzeżenia dotyczące analizy kodu.

Konfigurowanie kodu do analizowania

Użyj następującej opcji, aby skonfigurować, które części bazy kodu mają być uruchamiane w tej regule.

Tę opcję można skonfigurować tylko dla tej reguły, dla wszystkich reguł, do których ma ona zastosowanie, lub dla wszystkich reguł w tej kategorii (Projekt), których dotyczy. Aby uzyskać więcej informacji, zobacz Opcje konfiguracji reguły jakości kodu.

Uwzględnij określone powierzchnie interfejsu API

Możesz skonfigurować, na których częściach bazy kodu ma być uruchamiana ta reguła, na podstawie ich ułatwień dostępu. Aby na przykład określić, że reguła powinna być uruchamiana tylko na powierzchni niepublicznego interfejsu API, dodaj następującą parę klucz-wartość do pliku editorconfig w projekcie:

dotnet_code_quality.CAXXXX.api_surface = private, internal

Przykłady

Poniższy kod zawiera typ, który poprawnie implementuje IComparableelement . Komentarze kodu identyfikują metody spełniające różne reguły powiązane z Equals interfejsem IComparable i.

// 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)
    {
        ArgumentNullException.ThrowIfNull(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.", nameof(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.", nameof(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(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 CompareTo(other) == 0;
        }

        return false;
    }

    // Omitting getHashCode violates rule: OverrideGetHashCodeOnOverridingEquals.
    public override int GetHashCode()
    {
        char[] c = 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);
    }
}
Imports System.Globalization

Public Class RatingInformation
    Implements IComparable
    Implements IComparable(Of RatingInformation)

    Public Sub New(rating As String)
        ArgumentNullException.ThrowIfNull(rating)

        Dim v As String = rating.ToUpper(CultureInfo.InvariantCulture)
        If (v.Length <> 1 Or
            String.Compare(v, "C", StringComparison.Ordinal) > 0 Or
            String.Compare(v, "A", StringComparison.Ordinal) < 0) Then
            Throw New ArgumentException("Invalid rating value was specified.", NameOf(rating))
        End If

        Me.Rating = v
    End Sub

    Public ReadOnly Property Rating As String

    Public Function CompareTo(obj As Object) As Integer Implements IComparable.CompareTo
        If (obj Is Nothing) Then Return 1
        If (TypeOf obj IsNot RatingInformation) Then Return 0
        Dim other As RatingInformation = DirectCast(obj, RatingInformation)
        Return CompareTo(other)
    End Function

    Public Function CompareTo(other As RatingInformation) As Integer Implements IComparable(Of RatingInformation).CompareTo
        If (other Is Nothing) Then Return 1
        ' Ratings compare opposite To normal String order,
        ' so reverse the value returned by String.CompareTo.
        Return -String.Compare(Rating, other.Rating, StringComparison.OrdinalIgnoreCase)
    End Function

    Public Shared Operator =(one As RatingInformation, other As RatingInformation) As Boolean
        If (one Is Nothing) Then Return (other Is Nothing)
        If (other Is Nothing) Then Return False
        Return (one.Rating = other.Rating)
    End Operator

    Public Shared Operator <>(one As RatingInformation, other As RatingInformation) As Boolean
        If (one Is Nothing) Then Return (other IsNot Nothing)
        If (other Is Nothing) Then Return True
        Return (one.Rating <> other.Rating)
    End Operator

    Public Shared Operator <(one As RatingInformation, other As RatingInformation) As Boolean
        If (one Is Nothing) Then Return (other IsNot Nothing)
        If (other Is Nothing) Then Return False
        Return (one.Rating < other.Rating)
    End Operator

    Public Shared Operator >(one As RatingInformation, other As RatingInformation) As Boolean
        If (one Is Nothing) Then Return False
        If (other Is Nothing) Then Return True
        Return (one.Rating > other.Rating)
    End Operator

    Public Overrides Function Equals(obj As Object) As Boolean
        If ReferenceEquals(Me, obj) Then
            Return True
        End If

        If obj Is Nothing Then
            Return False
        End If

        Throw New NotImplementedException()
    End Function

    Public Overrides Function GetHashCode() As Integer
        Throw New NotImplementedException()
    End Function
End Class

Poniższy kod aplikacji testuje zachowanie przedstawionej IComparable wcześniej implementacji.

public class TestCompare
{
    public static void Main1036(params string[] args)
    {
        if (args.Length < 2)
        {
            return;
        }
        RatingInformation r1 = new(args[0]);
        RatingInformation r2 = new(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);
    }
}
Public Class TestCompare
    Public Shared Sub Main1036(ByVal args As String())
        If (args.Length < 2) Then
            Return
        End If
        Dim r1 As New RatingInformation(args(0))
        Dim r2 As New RatingInformation(args(1))
        Dim answer As String

        If (r1.CompareTo(r2) > 0) Then
            answer = "greater than"
        ElseIf (r1.CompareTo(r2) < 0) Then
            answer = "less than"
        Else
            answer = "equal to"
        End If

        Console.WriteLine("{0} is {1} {2}", r1.Rating, answer, r2.Rating)
    End Sub
End Class

Zobacz też