แนวทางปฏิบัติที่ดีในการโอเวอร์โหลดตัวดำเนินการ
ทำให้ตัวดำเนินการที่โอเวอร์โหลดมีความเข้าใจง่าย
แนวทางปฏิบัติที่ดีในการโอเวอร์โหลดตัวดำเนินการ เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การโอเวอร์โหลดตัวดำเนินการเลขคณิต
- การโอเวอร์โหลดตัวดำเนินการเปรียบเทียบ
- การแปลงที่ผู้ใช้กำหนด
- แนวทางปฏิบัติที่ดีในการโอเวอร์โหลดตัวดำเนินการ