0Pricing
C# Academy · Lekcja

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

Szybkie sprawdzenie: Aby nadać klasie równość wartościową działającą w HashSet i Dictionary, co należy zaimplementować?

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

  1. HashSet , SortedSet , Queue , Stack
  2. ConcurrentDictionary i kolekcje niezmienne
  3. Równość i haszowanie (wartość a referencja)
← Powrót do C# Academy