0Pricing
C# Academy · บทเรียน

ความเท่ากันและการแฮช (ค่ากับการอ้างอิง)

ทำความเข้าใจความเท่ากันของการอ้างอิงกับความเท่ากันของค่า เขียนทับ Equals/GetHashCode อย่างถูกต้อง ใช้ IEquatable และใช้ตัวเปรียบเทียบแบบกำหนดเองกับเซตและพจนานุกรม

ความเท่ากันและการแฮช (ค่ากับการอ้างอิง) เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 3 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 3 บทเรียน

พื้นฐานความเท่าเทียม

เป้าหมาย: ทำให้ความเท่าเทียมทำงานได้ตามที่คาดหวัง

  • ความเท่าเทียมตามการอ้างอิงเทียบกับความเท่าเทียมตามค่า
  • ข้อตกลงระหว่าง Equals กับ GetHashCode
  • IEquatable<T> เพื่อเพิ่มความเร็ว
  • ตัวเปรียบเทียบแบบกำหนดเองเมื่อไม่สามารถแก้ไขชนิดข้อมูลได้

จุดที่มักพลาดของความเท่าเทียมตามการอ้างอิง

คลาสมีความเท่าเทียมตามการอ้างอิงเป็นค่าเริ่มต้น ออบเจ็กต์สองรายการที่แยกจากกันแต่มีข้อมูลเดียวกันจะไม่เท่ากัน เว้นแต่คุณจะใช้งานความเท่าเทียมตามค่า

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)
  }
}

ใช้งานความเท่าเทียมตามค่าแล้ว

ใช้งาน IEquatable<T> แล้วเขียนทับ Equals และ GetHashCode ค่าแฮชต้องสอดคล้องกับความเท่าเทียม กล่าวคือ ออบเจ็กต์ที่เท่ากันต้องมีค่าแฮชเดียวกัน

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)
  }
}

ตัวเปรียบเทียบแบบกำหนดเอง

ส่ง IEqualityComparer<T> แบบกำหนดเองให้กับ HashSet/Dictionary เมื่อไม่สามารถเปลี่ยนชนิดข้อมูลนั้นเองได้

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
  }
}

ข้อตกลงและจุดที่มักพลาด

กฎ:

  • ถ้า Equals(a,b) เป็นจริง ⇒ GetHashCode(a) == GetHashCode(b)
  • ความเท่าเทียมควรมีคุณสมบัติเป็น สะท้อนตนเอง สมมาตร และถ่ายทอด
  • ควรใช้ฟิลด์ที่ไม่เปลี่ยนแปลงในการคำนวณแฮชเมื่อทำได้
  • อย่าใช้ค่าแบบสุ่มใน GetHashCode

ข้อสังเกตเกี่ยวกับโครงสร้างกับคลาส

โครงสร้างจะเปรียบเทียบตามฟิลด์เป็นค่าเริ่มต้น ซึ่งเป็นความหมายเชิงค่า ส่วนคลาสจะเปรียบเทียบตามการอ้างอิง เว้นแต่คุณจะใช้งานความเท่าเทียมตามค่า

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)
  }
}

ข้อตกลงความเท่าเทียมสำหรับคอลเลกชัน

ตรวจสอบอย่างรวดเร็ว: หากต้องการให้คลาสมีความเท่าเทียมตามค่าที่ใช้งานได้กับ HashSet และ Dictionary ควรใช้งานสิ่งใดบ้าง

สรุป

สรุป: คลาสต้องมี Equals/GetHashCode และมักต้องมี IEquatable<T> เพื่อให้มีความหมายเชิงค่า หากไม่สามารถแก้ไขชนิดข้อมูลได้ ให้ใช้ตัวเปรียบเทียบแบบกำหนดเอง

คำถามที่พบบ่อย

บทเรียน “ความเท่ากันและการแฮช (ค่ากับการอ้างอิง)” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ความเท่ากันและการแฮช (ค่ากับการอ้างอิง)” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 3 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ความเท่ากันและการแฮช (ค่ากับการอ้างอิง)”

ทำความเข้าใจความเท่ากันของการอ้างอิงกับความเท่ากันของค่า เขียนทับ Equals/GetHashCode อย่างถูกต้อง ใช้ IEquatable และใช้ตัวเปรียบเทียบแบบกำหนดเองกับเซตและพจนานุกรม คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 3 บทเรียน

บทเรียน “ความเท่ากันและการแฮช (ค่ากับการอ้างอิง)” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม

ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. HashSet , SortedSet , Queue , Stack
  2. ConcurrentDictionary และคอลเลกชันไม่เปลี่ยนแปลง
  3. ความเท่ากันและการแฮช (ค่ากับการอ้างอิง)
← กลับไปที่ C# Academy