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

แนวทางปฏิบัติที่ดีในการโอเวอร์โหลดตัวดำเนินการ

ทำให้ตัวดำเนินการที่โอเวอร์โหลดมีความเข้าใจง่าย

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

แนวปฏิบัติที่ดีสำหรับการโอเวอร์โหลดตัวดำเนินการ

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

โอเวอร์โหลดเฉพาะเมื่อความหมายชัดเจน

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

using System;

public struct Vector2
{
    public int X, Y;
    public Vector2(int x, int y) { X = x; Y = y; }
    // + means adding components: obvious and expected
    public static Vector2 operator +(Vector2 a, Vector2 b)
        => new Vector2(a.X + b.X, a.Y + b.Y);
    public override string ToString() => "(" + X + ", " + Y + ")";
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Vector2(1, 2) + new Vector2(3, 4));
    }
}

รักษาความคาดหมายทางคณิตศาสตร์

ทำให้ตัวดำเนินการสอดคล้องกับสัญชาตญาณ: a + b ควรเท่ากับ b + a เมื่อการคำนวณนั้นมีสมบัติการสลับที่ และ + ไม่ควรแอบทำการลบ ความประหลาดใจในจุดนี้ทำให้เกิดข้อผิดพลาดที่ตรวจพบได้ยาก

using System;

public struct Money
{
    public decimal Amount;
    public Money(decimal a) { Amount = a; }
    public static Money operator +(Money a, Money b) => new Money(a.Amount + b.Amount);
    public override string ToString() => "$" + Amount;
}

public class Program
{
    public static void Main()
    {
        var x = new Money(3);
        var y = new Money(5);
        Console.WriteLine((x + y).Amount == (y + x).Amount); // commutative
    }
}

รักษาความสมมาตรและการจับคู่

ภาษาโปรแกรมจะจับคู่ตัวดำเนินการบางชนิดให้ แต่คุณควรรักษาความสมมาตรทางตรรกะไว้ด้วย หากเพิ่ม + ก็ควรพิจารณาเพิ่ม - หากเพิ่ม < ก็ควรมี > และรูปแบบที่มีเครื่องหมายเท่ากับด้วย ชุดตัวดำเนินการที่ทำไม่ครบทำให้ผู้ใช้หงุดหงิด

using System;

public struct Length
{
    public int Mm;
    public Length(int mm) { Mm = mm; }
    public static Length operator +(Length a, Length b) => new Length(a.Mm + b.Mm);
    public static Length operator -(Length a, Length b) => new Length(a.Mm - b.Mm);
    public override string ToString() => Mm + "mm";
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Length(50) + new Length(20));
        Console.WriteLine(new Length(50) - new Length(20));
    }
}

อย่าเปลี่ยนแปลงตัวถูกดำเนินการ

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

using System;

public struct Vector2
{
    public int X, Y;
    public Vector2(int x, int y) { X = x; Y = y; }
    public static Vector2 operator +(Vector2 a, Vector2 b)
        => new Vector2(a.X + b.X, a.Y + b.Y); // returns new, mutates nothing
    public override string ToString() => "(" + X + ", " + Y + ")";
}

public class Program
{
    public static void Main()
    {
        var a = new Vector2(1, 1);
        var unused = a + new Vector2(9, 9);
        Console.WriteLine("a is still " + a);
    }
}

ทำให้ == สอดคล้องกับ Equals

แนวปฏิบัติที่ดีซึ่งควรทำเป็นประจำคือ เมื่อโอเวอร์โหลด == ให้โอเวอร์ไรด์ Equals และ GetHashCode ด้วย เพื่อให้เส้นทางการตรวจสอบความเท่ากันทั้งหมดให้ผลตรงกัน ความไม่สอดคล้องในจุดนี้เป็นหนึ่งในข้อผิดพลาดของตัวดำเนินการที่พบบ่อยที่สุด

using System;

public struct Point
{
    public int X, Y;
    public Point(int x, int y) { X = x; Y = y; }
    public override bool Equals(object obj) => obj is Point p && p.X == X && p.Y == Y;
    public override int GetHashCode() => HashCode.Combine(X, Y);
    public static bool operator ==(Point a, Point b) => a.Equals(b);
    public static bool operator !=(Point a, Point b) => !a.Equals(b);
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Point(1, 1) == new Point(1, 1));
    }
}

จัดเตรียมทางเลือกที่มีชื่อเรียก

ภาษาบางภาษาและเครื่องมือวิเคราะห์บางชนิดไม่สามารถใช้ตัวดำเนินการได้ การมีเมธอดที่มีชื่อเรียก เช่น Add ควบคู่กับ + จะช่วยให้ทำงานร่วมกันได้ดีขึ้นและค้นพบความสามารถได้ง่ายขึ้น

using System;

public struct Vector2
{
    public int X, Y;
    public Vector2(int x, int y) { X = x; Y = y; }
    public Vector2 Add(Vector2 other) => new Vector2(X + other.X, Y + other.Y);
    public static Vector2 operator +(Vector2 a, Vector2 b) => a.Add(b);
    public override string ToString() => "(" + X + ", " + Y + ")";
}

public class Program
{
    public static void Main()
    {
        var v = new Vector2(1, 2);
        Console.WriteLine(v.Add(new Vector2(3, 4)));
        Console.WriteLine(v + new Vector2(3, 4));
    }
}

เลือกใช้ตัวดำเนินการกับชนิดข้อมูลที่มีลักษณะเหมือนค่า

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

using System;

public readonly struct Rational
{
    public readonly int Num, Den;
    public Rational(int n, int d) { Num = n; Den = d; }
    public static Rational operator *(Rational a, Rational b)
        => new Rational(a.Num * b.Num, a.Den * b.Den);
    public override string ToString() => Num + "/" + Den;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Rational(1, 2) * new Rational(2, 3));
    }
}

จัดทำเอกสารสำหรับกรณีขอบ

อธิบายให้ชัดเจนว่าตัวดำเนินการจัดการค่าพิเศษอย่างไร เช่น การหารด้วยศูนย์ ตัวถูกดำเนินการที่เป็นค่าว่างสำหรับคลาส หรือค่าล้น การจัดการที่คาดเดาได้จะป้องกันไม่ให้ผู้เรียกพบปัญหาโดยไม่ทันตั้งตัว

using System;

public struct SafeDiv
{
    public int Value;
    public SafeDiv(int v) { Value = v; }
    // Documented behavior: dividing by zero yields zero
    public static SafeDiv operator /(SafeDiv a, SafeDiv b)
        => new SafeDiv(b.Value == 0 ? 0 : a.Value / b.Value);
    public override string ToString() => Value.ToString();
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new SafeDiv(10) / new SafeDiv(0));
        Console.WriteLine(new SafeDiv(10) / new SafeDiv(2));
    }
}

ชุดตัวดำเนินการที่มีแบบแผน

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

using System;

public readonly struct Vec
{
    public readonly double X, Y;
    public Vec(double x, double y) { X = x; Y = y; }
    public static Vec operator +(Vec a, Vec b) => new Vec(a.X + b.X, a.Y + b.Y);
    public static Vec operator -(Vec a, Vec b) => new Vec(a.X - b.X, a.Y - b.Y);
    public static Vec operator *(Vec a, double k) => new Vec(a.X * k, a.Y * k);
    public override string ToString() => "(" + X + ", " + Y + ")";
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine((new Vec(1, 2) + new Vec(3, 4)) * 2);
    }
}

ลองทำด้วยตนเอง

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

using System;

public readonly struct Meters : IEquatable<Meters>
{
    public readonly double Value;
    public Meters(double v) { Value = v; }
    public Meters Add(Meters other) => new Meters(Value + other.Value);
    public static Meters operator +(Meters a, Meters b) => a.Add(b);
    public bool Equals(Meters other) => Value == other.Value;
    public override bool Equals(object obj) => obj is Meters m && Equals(m);
    public override int GetHashCode() => Value.GetHashCode();
    public static bool operator ==(Meters a, Meters b) => a.Equals(b);
    public static bool operator !=(Meters a, Meters b) => !a.Equals(b);
    public override string ToString() => Value + "m";
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Meters(3) + new Meters(4));
        Console.WriteLine(new Meters(7) == new Meters(7));
    }
}

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

นำแนวปฏิบัติที่ดีสำหรับตัวดำเนินการมาใช้

สรุปทบทวน

การโอเวอร์โหลดตัวดำเนินการที่ดีต้องมีแบบแผนและคาดเดาได้

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

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

บทเรียน “แนวทางปฏิบัติที่ดีในการโอเวอร์โหลดตัวดำเนินการ” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “แนวทางปฏิบัติที่ดีในการโอเวอร์โหลดตัวดำเนินการ”

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

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

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

บทเรียน “แนวทางปฏิบัติที่ดีในการโอเวอร์โหลดตัวดำเนินการ” ใช้เวลานานแค่ไหน

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

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

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

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

  1. การโอเวอร์โหลดตัวดำเนินการเลขคณิต
  2. การโอเวอร์โหลดตัวดำเนินการเปรียบเทียบ
  3. การแปลงที่ผู้ใช้กำหนด
  4. แนวทางปฏิบัติที่ดีในการโอเวอร์โหลดตัวดำเนินการ
← กลับไปที่ C# Academy