CA1036: Přepište metody srovnatelných typů

Vlastnost Hodnota
ID pravidla CA1036
Název Přepište metody u srovnatelných typů
Kategorie Návrh
Oprava způsobující chybu nebo chybu způsobující chybu Nenarušující
Povoleno ve výchozím nastavení v .NET 8 No

Příčina

Typ implementuje System.IComparable rozhraní a nepřepíše System.Object.Equals nebo nepřetěžuje operátor specifický pro jazyk pro rovnost, nerovnost, menší než nebo větší než. Pravidlo neohlásí porušení, pokud typ dědí pouze implementaci rozhraní.

Ve výchozím nastavení toto pravidlo sleduje jenom externě viditelné typy, ale dá se konfigurovat.

Popis pravidla

Typy definující vlastní pořadí řazení implementují IComparable rozhraní. Metoda CompareTo vrátí celočíselnou hodnotu, která označuje správné pořadí řazení pro dvě instance typu. Toto pravidlo identifikuje typy, které nastavují pořadí řazení. Nastavení pořadí řazení znamená, že běžný význam rovnosti, nerovnosti, menší než a větší než neplatí. Když zadáte implementaci IComparable, musíte obvykle také přepsat Equals tak, aby vrátil hodnoty, které jsou konzistentní s CompareTo. Pokud přepíšete Equals a kódujete v jazyce, který podporuje přetížení operátorů, měli byste také poskytnout operátory, které jsou konzistentní s Equals.

Jak opravit porušení

Chcete-li opravit porušení tohoto pravidla, přepište Equals. Pokud váš programovací jazyk podporuje přetížení operátorů, zadejte následující operátory:

  • 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

Kdy potlačit upozornění

Je bezpečné potlačit upozornění z pravidla CA1036, pokud porušení je způsobeno chybějícími operátory a váš programovací jazyk nepodporuje přetížení operátoru. Pokud zjistíte, že implementace operátorů nemá smysl v kontextu aplikace, je také bezpečné potlačit upozornění z tohoto pravidla, když se aktivuje u jiných operátorů rovnosti než op_Equality. Pokud však přepíšete , měli byste je vždy přepsat op_Equality Object.Equalsa == operátor .

Potlačení upozornění

Pokud chcete pouze potlačit jedno porušení, přidejte do zdrojového souboru direktivy preprocesoru, abyste pravidlo zakázali a znovu povolili.

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

Pokud chcete pravidlo pro soubor, složku nebo projekt zakázat, nastavte jeho závažnost v none konfiguračním souboru.

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

Další informace naleznete v tématu Jak potlačit upozornění analýzy kódu.

Konfigurace kódu pro analýzu

Pomocí následující možnosti nakonfigurujte, ve kterých částech základu kódu se má toto pravidlo spouštět.

Tuto možnost můžete nakonfigurovat jenom pro toto pravidlo, pro všechna pravidla, která platí, nebo pro všechna pravidla v této kategorii (Návrh), na která platí. Další informace naleznete v tématu Možnosti konfigurace pravidla kvality kódu.

Zahrnutí konkrétních povrchů rozhraní API

Na základě přístupnosti můžete nakonfigurovat, na kterých částech základu kódu se má toto pravidlo spouštět. Pokud chcete například určit, že pravidlo by se mělo spouštět jenom na neveřejné ploše rozhraní API, přidejte do souboru .editorconfig v projektu následující pár klíč-hodnota:

dotnet_code_quality.CAXXXX.api_surface = private, internal

Příklady

Následující kód obsahuje typ, který správně implementuje IComparable. Komentáře kódu identifikují metody, které vyhovují různým pravidlům, která souvisejí s Equals rozhraním 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)
    {
        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

Následující kód aplikace testuje chování IComparable implementace, která byla zobrazena dříve.

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

Viz také