Równość i haszowanie (wartość a referencja)
Proszę zrozumieć równość referencji i równość wartości, poprawnie przesłaniać Equals/GetHashCode, implementować IEquatable oraz używać własnych comparerów ze zbiorami i słownikami.
Równość i haszowanie (wartość a referencja) to bezpłatna lekcja C# Academy na CoddyKit. To lekcja 3 z 3. 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 3 lekcji w sumie.
Podstawy równości
Cel: Zapewnić działanie równości zgodnie z oczekiwaniami.
- Równość referencyjna a równość wartościowa
- Kontrakt Equals + GetHashCode
- IEquatable<T> dla większej szybkości
- Niestandardowe komparatory, gdy nie można zmodyfikować typu
Pułapka równości referencyjnej
Klasy domyślnie korzystają z równości referencyjnej. Dwa oddzielne obiekty z takimi samymi danymi nie są równe, chyba że zaimplementują Państwo równość wartościową.
using System;
using System.Collections.Generic;
public sealed class Point // no overrides
{
public int X;
public int Y;
public Point(int x, int y){ X = x; Y = y; }
}
public class Program
{
public static void Main(string[] args)
{
Point a = new Point(1, 2);
Point b = new Point(1, 2);
Console.WriteLine("a == b ? " + (a == b)); // reference equality: False
Console.WriteLine("a.Equals(b) ? " + a.Equals(b)); // False
HashSet<Point> set = new HashSet<Point>();
set.Add(a);
Console.WriteLine("Contains b? " + set.Contains(b)); // False (unexpected)
}
}
Zaimplementowana równość wartościowa
Zaimplementuj IEquatable<T> oraz przesłoń Equals i GetHashCode. Skrót musi być zgodny z równością: równe obiekty → ten sam skrót.
using System;
using System.Collections.Generic;
public sealed class ValuePoint : IEquatable<ValuePoint>
{
public int X;
public int Y;
public ValuePoint(int x, int y){ X = x; Y = y; }
public bool Equals(ValuePoint other)
{
if (ReferenceEquals(other, null)) return false;
if (ReferenceEquals(this, other)) return true;
return X == other.X && Y == other.Y;
}
public override bool Equals(object obj)
{
return Equals(obj as ValuePoint);
}
public override int GetHashCode()
{
// Simple, stable combination (avoid randomness)
unchecked
{
int hash = 17;
hash = hash * 31 + X.GetHashCode();
hash = hash * 31 + Y.GetHashCode();
return hash;
}
}
}
public class Program
{
public static void Main(string[] args)
{
ValuePoint a = new ValuePoint(1, 2);
ValuePoint b = new ValuePoint(1, 2);
Console.WriteLine("a.Equals(b)? " + a.Equals(b)); // True
HashSet<ValuePoint> set = new HashSet<ValuePoint>();
set.Add(a);
Console.WriteLine("Contains b? " + set.Contains(b)); // True (value semantics)
}
}
Niestandardowy komparator
Przekaż niestandardowy IEqualityComparer<T> do HashSet/Dictionary, gdy nie możesz zmienić samego typu.
using System;
using System.Collections.Generic;
public sealed class Person // imagine from a library; cannot edit
{
public string Name;
public int BirthYear;
public Person(string name, int year){ Name = name; BirthYear = year; }
}
public sealed class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person a, Person b)
{
if (ReferenceEquals(a, b)) return true;
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false;
return a.Name == b.Name && a.BirthYear == b.BirthYear;
}
public int GetHashCode(Person p)
{
if (ReferenceEquals(p, null)) return 0;
unchecked
{
int h = 23;
h = h * 31 + (p.Name == null ? 0 : p.Name.GetHashCode());
h = h * 31 + p.BirthYear.GetHashCode();
return h;
}
}
}
public class Program
{
public static void Main(string[] args)
{
Person p1 = new Person("Ada", 1815);
Person p2 = new Person("Ada", 1815);
HashSet<Person> set = new HashSet<Person>(new PersonComparer());
set.Add(p1);
Console.WriteLine("Contains p2? " + set.Contains(p2)); // True via comparer
}
}
Kontrakt i pułapki
Zasady:
- Jeśli Equals(a,b) ma wartość true ⇒ GetHashCode(a) == GetHashCode(b)
- Równość powinna być zwrotna, symetryczna i przechodnia
- W miarę możliwości używaj niemutowalnych pól do obliczania skrótu
- Nie używaj losowych wartości w GetHashCode
Struktura a klasa
Struktury domyślnie porównują pola (semantyka wartości). Klasy porównują referencje, chyba że zaimplementują Państwo równość wartościową.
using System;
public struct PointS // struct: value type
{
public int X;
public int Y;
public PointS(int x, int y){ X = x; Y = y; }
}
public class Program
{
public static void Main(string[] args)
{
PointS a = new PointS(1, 2);
PointS b = new PointS(1, 2);
Console.WriteLine("Struct equality: " + a.Equals(b)); // True by default (field-wise)
}
}
Kontrakt równości dla kolekcji
Podsumowanie
Podsumowanie: Klasy potrzebują Equals/GetHashCode (a często także IEquatable<T>) do zapewnienia semantyki wartości; gdy typów nie można edytować, należy użyć niestandardowych komparatorów.
Często zadawane pytania
Czy lekcja „Równość i haszowanie (wartość a referencja)” jest bezpłatna?
Tak — pełny tekst „Równość i haszowanie (wartość a referencja)” 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 3 lekcji w sumie.
Co nauczysz się w „Równość i haszowanie (wartość a referencja)”?
Proszę zrozumieć równość referencji i równość wartości, poprawnie przesłaniać Equals/GetHashCode, implementować IEquatable oraz używać własnych comparerów ze zbiorami i słownikami. Ć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 3 z 3.
Ile czasu zajmuje lekcja „Równość i haszowanie (wartość a referencja)”?
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
- HashSet , SortedSet , Queue , Stack
- ConcurrentDictionary i kolekcje niezmienne
- Równość i haszowanie (wartość a referencja)