Implementowanie IEquatable
Zapewniaj bezpieczne typowo porównywanie równości za pomocą Equals.
Implementowanie IEquatable to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C# Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C# Academy zawiera 4 lekcji w sumie.
Dlaczego IEquatable<T>?
IEquatable<T> udostępnia silnie typowaną metodę Equals(T other). Eliminuje boxing dla typów wartości i konieczność rzutowania dla typów referencyjnych, dzięki czemu porównywanie równości jest szybsze i bardziej przejrzyste.
Implementowanie interfejsu
Należy zaimplementować IEquatable<T> i udostępnić Equals(T other). Należy również przesłonić odziedziczone object.Equals i GetHashCode, aby zachować spójność wszystkich ścieżek.
using System;
public struct Point : IEquatable<Point>
{
public int X, Y;
public Point(int x, int y) { X = x; Y = y; }
public bool Equals(Point other) => X == other.X && Y == other.Y;
public override bool Equals(object obj) => obj is Point p && Equals(p);
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Point(1, 2).Equals(new Point(1, 2)));
}
}Unikanie boxingu dla struktur
Niesparametryzowana metoda object.Equals(object) wykonuje boxing argumentu będącego strukturą. Typowana metoda Equals(Point other) z IEquatable<T> przyjmuje strukturę bezpośrednio, więc nie dochodzi do alokacji.
using System;
public struct Point : IEquatable<Point>
{
public int X, Y;
public Point(int x, int y) { X = x; Y = y; }
public bool Equals(Point other) => X == other.X && Y == other.Y; // no boxing
public override bool Equals(object obj) => obj is Point p && Equals(p);
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
public static void Main()
{
Point a = new Point(3, 4);
Point b = new Point(3, 4);
Console.WriteLine(a.Equals(b)); // calls the typed overload
}
}Kolekcje korzystają z IEquatable<T>
List<T>.Contains, Dictionary i HashSet preferują IEquatable<T>.Equals, gdy jest dostępna. Zaimplementowanie tego interfejsu sprawia, że te operacje są wydajne i poprawne dla danego typu.
using System;
using System.Collections.Generic;
public struct Point : IEquatable<Point>
{
public int X, Y;
public Point(int x, int y) { X = x; Y = y; }
public bool Equals(Point other) => X == other.X && Y == other.Y;
public override bool Equals(object obj) => obj is Point p && Equals(p);
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
public static void Main()
{
var list = new List<Point> { new Point(0, 0), new Point(1, 1) };
Console.WriteLine(list.Contains(new Point(1, 1)));
}
}Typy referencyjne a IEquatable<T>
Klasy również mogą implementować IEquatable<T>. Typowana metoda Equals eliminuje konieczność rzutowania, a parametr referencyjny należy jawnie obsłużyć w przypadku null.
using System;
public class Person : IEquatable<Person>
{
public string Name;
public Person(string name) { Name = name; }
public bool Equals(Person other) => other != null && other.Name == Name;
public override bool Equals(object obj) => Equals(obj as Person);
public override int GetHashCode() => Name?.GetHashCode() ?? 0;
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Person("Ada").Equals(new Person("Ada")));
Console.WriteLine(new Person("Ada").Equals((Person)null));
}
}Kierowanie object.Equals przez metodę typowaną
Przejrzysty wzorzec polega na zapisaniu porównania tylko raz w Equals(T) i delegowaniu do niego z poziomu object.Equals. Gwarantuje to identyczne działanie obu metod.
using System;
public struct Id : IEquatable<Id>
{
public int Value;
public Id(int value) { Value = value; }
public bool Equals(Id other) => Value == other.Value;
public override bool Equals(object obj) => obj is Id other && Equals(other);
public override int GetHashCode() => Value;
}
public class Program
{
public static void Main()
{
object boxed = new Id(5);
Console.WriteLine(boxed.Equals(new Id(5)));
}
}Porównywanie wielu pól
W przypadku typów zawierających kilka pól należy porównać je wszystkie w metodzie Equals. Dwie instancje są równe tylko wtedy, gdy zgadza się każde istotne pole.
using System;
public struct DateRange : IEquatable<DateRange>
{
public int Start, End;
public DateRange(int start, int end) { Start = start; End = end; }
public bool Equals(DateRange other) => Start == other.Start && End == other.End;
public override bool Equals(object obj) => obj is DateRange d && Equals(d);
public override int GetHashCode() => HashCode.Combine(Start, End);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new DateRange(1, 5).Equals(new DateRange(1, 5)));
Console.WriteLine(new DateRange(1, 5).Equals(new DateRange(1, 6)));
}
}Łączenie z operatorami
Należy połączyć IEquatable<T> z operatorami == i !=, aby użytkownicy mogli porównywać wartości przy użyciu obu składni, opartych na tym samym typowanym porównaniu.
using System;
public struct Money : IEquatable<Money>
{
public decimal Amount;
public Money(decimal a) { Amount = a; }
public bool Equals(Money other) => Amount == other.Amount;
public override bool Equals(object obj) => obj is Money m && Equals(m);
public override int GetHashCode() => Amount.GetHashCode();
public static bool operator ==(Money a, Money b) => a.Equals(b);
public static bool operator !=(Money a, Money b) => !a.Equals(b);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Money(9.99m) == new Money(9.99m));
}
}Rekordy implementują to automatycznie
Element record języka C# generuje automatycznie IEquatable<T>, oparte na wartościach metody Equals oraz GetHashCode. Gdy potrzebne jest tylko porównywanie wartości, rekord pozwala uniknąć pisania kodu szablonowego.
using System;
public record Point(int X, int Y);
public class Program
{
public static void Main()
{
var a = new Point(1, 2);
var b = new Point(1, 2);
Console.WriteLine(a.Equals(b));
Console.WriteLine(a == b);
}
}Kiedy implementować ręcznie
IEquatable<T> należy implementować ręcznie, gdy potrzebne są niestandardowe reguły porównywania równości, takie jak niewrażliwe na wielkość liter nazwy lub pomijanie określonych pól, których nie można bezpośrednio wyrazić za pomocą rekordu.
using System;
public class CaseInsensitiveTag : IEquatable<CaseInsensitiveTag>
{
public string Name;
public CaseInsensitiveTag(string name) { Name = name; }
public bool Equals(CaseInsensitiveTag other)
=> other != null && string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase);
public override bool Equals(object obj) => Equals(obj as CaseInsensitiveTag);
public override int GetHashCode() => Name?.ToLowerInvariant().GetHashCode() ?? 0;
}
public class Program
{
public static void Main()
{
Console.WriteLine(new CaseInsensitiveTag("Red").Equals(new CaseInsensitiveTag("RED")));
}
}Wypróbuj samodzielnie
Należy zaimplementować IEquatable<T> dla niewielkiego typu referencyjnego i skierować każdą ścieżkę porównywania równości przez metodę typowaną.
using System;
using System.Collections.Generic;
public class Coordinate : IEquatable<Coordinate>
{
public int Lat, Lng;
public Coordinate(int lat, int lng) { Lat = lat; Lng = lng; }
public bool Equals(Coordinate other) => other != null && other.Lat == Lat && other.Lng == Lng;
public override bool Equals(object obj) => Equals(obj as Coordinate);
public override int GetHashCode() => HashCode.Combine(Lat, Lng);
}
public class Program
{
public static void Main()
{
var set = new HashSet<Coordinate> { new Coordinate(10, 20) };
Console.WriteLine(set.Contains(new Coordinate(10, 20)));
}
}Szybkie sprawdzenie
Należy przypomnieć sobie zaletę IEquatable
Podsumowanie
IEquatable<T> zapewnia bezpieczne typowo porównywanie równości.
- Udostępnia
Equals(T other), eliminując boxing dla struktur i rzutowanie dla klas. - Kolekcje preferują ten interfejs, aby wyszukiwanie było poprawne i wydajne.
- Należy kierować
object.Equalsi operatory przez metodę typowaną. - Rekordy implementują go automatycznie; w przypadku niestandardowych reguł należy zaimplementować go ręcznie.
Ucz się C# dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 93
- Lekcje
- 346
Często zadawane pytania
Czy lekcja „Implementowanie IEquatable” jest bezpłatna?
Tak — pełny tekst „Implementowanie IEquatable” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C# Academy, przejdź na CoddyKit PRO. Kurs C# Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Implementowanie IEquatable”?
Zapewniaj bezpieczne typowo porównywanie równości za pomocą Equals. Ćwiczysz C# Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć C# Academy?
Nie wymagamy żadnego doświadczenia. C# Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Implementowanie IEquatable”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji C# Academy?
Tak. Każda lekcja C# Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Implementowanie IEquatable
- Przesłanianie GetHashCode
- Implementowanie IComparable
- IComparer do niestandardowego sortowania