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

ความเท่ากันของค่าและการแยกโครงสร้าง

ทำความเข้าใจความเท่ากันเชิงโครงสร้างของ record เขียนทับ Equals/GetHashCode และใช้รูปแบบการแยกโครงสร้าง

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

เจาะลึกการเปรียบเทียบความเท่ากันของเรคคอร์ด

เรคคอร์ดจะสร้าง Equals, GetHashCode และตัวดำเนินการ ==/!= โดยอัตโนมัติ เพื่อเปรียบเทียบคุณสมบัติทั้งหมดเชิงโครงสร้าง การทำความเข้าใจกลไกนี้ช่วยให้ใช้งานเรคคอร์ดได้อย่างถูกต้องและเขียนทับการทำงานเมื่อจำเป็น

กลไกการเปรียบเทียบความเท่ากันที่สร้างโดยอัตโนมัติ

คอมไพเลอร์จะสร้างโค้ดที่เปรียบเทียบคุณสมบัติแต่ละรายการด้วย EqualityComparer<T>.Default สำหรับคุณสมบัติชนิดอ้างอิง นี่คือ Equals ของคุณสมบัตินั้นเอง ไม่ใช่การเปรียบเทียบการอ้างอิง

public record Address(string Street, string City);
public record Person(string Name, Address HomeAddress);

var a = new Person("Alice", new Address("123 Main", "NYC"));
var b = new Person("Alice", new Address("123 Main", "NYC"));

Console.WriteLine(a == b); // True — deep structural equality
// Equality checks:
// Name: "Alice" == "Alice" ✓
// HomeAddress: compares Address properties recursively ✓

การเขียนทับการเปรียบเทียบความเท่ากันที่สร้างขึ้น

คุณสามารถเขียนทับ Equals และ GetHashCode ในเรคคอร์ดได้เมื่อต้องการตรรกะการเปรียบเทียบความเท่ากันแบบกำหนดเอง เช่น เปรียบเทียบเฉพาะคุณสมบัติบางส่วน

public record Customer(int Id, string Name, string Email)
{
    // Consider customers equal if they have the same ID,
    // regardless of name or email changes
    public virtual bool Equals(Customer? other)
        => other is not null && Id == other.Id;

    public override int GetHashCode() => Id.GetHashCode();
}

เรคคอร์ดในคอลเลกชัน

เนื่องจากเรคคอร์ดนำ IEquatable<T> มาใช้พร้อมการเปรียบเทียบความเท่ากันเชิงโครงสร้าง จึงทำงานได้อย่างถูกต้องเมื่อใช้เป็นคีย์ของพจนานุกรม ใน HashSets และร่วมกับ Distinct และ GroupBy ของ LINQ

public record Tag(string Name, string Color);

var tags = new List<Tag>
{
    new("C#", "blue"), new("C#", "blue"), new("dotnet", "purple")
};

var unique = tags.Distinct().ToList(); // [Tag(C#, blue), Tag(dotnet, purple)]

var set = new HashSet<Tag>(tags);
set.Contains(new Tag("C#", "blue")); // true

การแยกโครงสร้าง: พื้นฐาน

เรคคอร์ดแบบระบุตำแหน่งจะสร้างเมธอด Deconstruct โดยอัตโนมัติ พร้อมพารามิเตอร์ out สำหรับคุณสมบัติแบบระบุตำแหน่งแต่ละรายการ ทำให้สามารถแยกค่าออกในลักษณะคล้ายทูเพิลได้

public record Point(double X, double Y);
public record RGB(byte R, byte G, byte B);

var p = new Point(3.0, 4.0);
var (x, y) = p; // deconstruct
Console.WriteLine($"{x}, {y}");

var color = new RGB(255, 128, 0);
var (r, g, b) = color;
Console.WriteLine($"R={r}, G={g}, B={b}");

การแยกโครงสร้างในการจับคู่รูปแบบ

การแยกโครงสร้างเรคคอร์ดในนิพจน์ switch ช่วยให้จับคู่รูปแบบตามตำแหน่งได้อย่างทรงพลัง

public record Point(int X, int Y);

string Classify(Point p) => p switch
{
    (0, 0)           => "Origin",
    (int x, 0)       => $"X-axis at {x}",
    (0, int y)       => $"Y-axis at {y}",
    (int x, int y) when x == y => $"Diagonal at {x}",
    (int x, int y)   => $"Point ({x}, {y})"
};

Console.WriteLine(Classify(new Point(0, 0)));  // Origin
Console.WriteLine(Classify(new Point(3, 3)));  // Diagonal at 3

เมธอด Deconstruct แบบกำหนดเอง

เรคคอร์ดที่ไม่ใช่แบบระบุตำแหน่งและคลาสใด ๆ สามารถเพิ่มเมธอด Deconstruct เองได้ นอกจากนี้ เมธอดส่วนขยายยังสามารถเพิ่มความสามารถในการแยกโครงสร้างให้กับชนิดที่มีอยู่แล้วได้

public class Temperature
{
    public double Celsius { get; }
    public Temperature(double c) => Celsius = c;

    // Manual Deconstruct
    public void Deconstruct(out double celsius, out double fahrenheit)
    {
        celsius    = Celsius;
        fahrenheit = Celsius * 9 / 5 + 32;
    }
}

var t = new Temperature(100);
var (c, f) = t;
Console.WriteLine($"{c}°C = {f}°F"); // 100°C = 212°F

การละเว้นบางส่วนของการแยกโครงสร้าง

ใช้ดิสคาร์ด (_) เพื่อละเว้นส่วนที่ไม่ต้องการจากการแยกโครงสร้าง ซึ่งคล้ายกับดิสคาร์ดของทูเพิล

public record Order(int Id, string Customer, decimal Total, DateTime Date);

var order = new Order(42, "Alice", 99.99m, DateTime.UtcNow);

// Only want Id and Total
var (id, _, total, _) = order;
Console.WriteLine($"Order {id}: ${total}");

// In switch: ignore some fields
string Classify(Order o) => o switch
{
    (_, _, > 1000, _) => "High value",
    (_, _, > 100, _)  => "Medium value",
    _                 => "Low value"
};

รูปแบบคุณสมบัติกับรูปแบบตามตำแหน่ง

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

public record Order(int Id, string Status, decimal Total);

// Positional pattern:
string DescribePos(Order o) => o switch
{
    (_, "Shipped", > 100) => "High-value shipped",
    _                     => "Other"
};

// Property pattern (more readable):
string DescribeProp(Order o) => o switch
{
    { Status: "Shipped", Total: > 100 } => "High-value shipped",
    { Status: "Cancelled" }             => "Cancelled",
    _                                   => "Other"
};

การใช้งานจริง: รูปแบบชนิด Result

ชนิด Result ในรูปแบบยูเนียนที่แยกแยะได้ โดยใช้เรคคอร์ดและการแยกโครงสร้าง เป็นแนวทางเชิงฟังก์ชันสำหรับการจัดการข้อผิดพลาด

public abstract record Result<T>;
public record Success<T>(T Value) : Result<T>;
public record Failure<T>(string Error) : Result<T>;

Result<Order> PlaceOrder(CreateOrderRequest req)
{
    if (req.Quantity <= 0)
        return new Failure<Order>("Quantity must be positive");
    var order = CreateOrder(req);
    return new Success<Order>(order);
}

// Consuming:
var result = PlaceOrder(request);
switch (result)
{
    case Success<Order>(var order):
        return Results.Created($"/orders/{order.Id}", order);
    case Failure<Order>(var error):
        return Results.BadRequest(error);
}

การตรวจสอบอย่างรวดเร็ว

การใช้ดิสคาร์ด (_) ในนิพจน์การแยกโครงสร้างทำอะไร

สรุป: การเปรียบเทียบเชิงค่าและการแยกโครงสร้าง

ประเด็นสำคัญ:

  • การตรวจสอบความเท่ากันที่สร้างโดยอัตโนมัติจะเปรียบเทียบพร็อพเพอร์ตีทั้งหมดตามโครงสร้างแบบเรียกซ้ำ
  • แทนที่ Equals/GetHashCode ในเรคคอร์ดเพื่อกำหนดตรรกะเอกลักษณ์แบบกำหนดเอง
  • เรคคอร์ดทำงานได้ถูกต้องเมื่อใช้เป็นสมาชิกของ HashSet และคีย์ของพจนานุกรม
  • เรคคอร์ดแบบกำหนดตำแหน่งจะสร้าง Deconstruct โดยอัตโนมัติ — ใช้ var (a, b) = record
  • ดิสคาร์ด (_) ใช้ละทิ้งค่าแบบกำหนดตำแหน่งที่ไม่ต้องการในการแยกโครงสร้าง
  • รูปแบบพร็อพเพอร์ตี ({Status: "X"}) อ่านเข้าใจได้ง่ายกว่ารูปแบบตามตำแหน่งสำหรับเรคคอร์ดที่ซับซ้อน

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

บทเรียน “ความเท่ากันของค่าและการแยกโครงสร้าง” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “ความเท่ากันของค่าและการแยกโครงสร้าง”

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

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

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

บทเรียน “ความเท่ากันของค่าและการแยกโครงสร้าง” ใช้เวลานานแค่ไหน

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

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

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

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

  1. ชนิด Record: พื้นฐานและไวยากรณ์
  2. ความไม่เปลี่ยนแปลงด้วย init และ with
  3. ความเท่ากันของค่าและการแยกโครงสร้าง
  4. Records ในการออกแบบที่ขับเคลื่อนด้วยโดเมน
← กลับไปที่ C# Academy