Nạp chồng toán tử
Thuộc phần Lập trình hướng đối tượng trong hành trình C# của Coddy. Bài học 36 trên 70.
Nạp chồng toán tử cho phép bạn định nghĩa cách các toán tử như +, - hoặc == hoạt động với các lớp tùy chỉnh của bạn. Thay vì gọi các phương thức như point1.Add(point2), bạn có thể viết cách tự nhiên hơn là point1 + point2.
Để nạp chồng toán tử, bạn tạo một phương thức tĩnh đặc biệt bằng từ khóa operator:
public class Vector
{
public int X { get; }
public int Y { get; }
public Vector(int x, int y)
{
X = x;
Y = y;
}
public static Vector operator +(Vector a, Vector b)
{
return new Vector(a.X + b.X, a.Y + b.Y);
}
}Giờ bạn có thể cộng vector một cách tự nhiên:
Vector v1 = new Vector(3, 4);
Vector v2 = new Vector(1, 2);
Vector v3 = v1 + v2; // X = 4, Y = 6Phương thức phải là public static, và ít nhất một tham số phải có kiểu là kiểu chứa phương thức đó. Bạn có thể nạp chồng các toán tử số học (+, -, *, /), toán tử so sánh (==, !=, <, >) và toán tử một ngôi (-, !, ++).
Lưu ý rằng bạn phải nạp chồng các toán tử so sánh theo từng cặp: nếu định nghĩa ==, bạn cũng phải định nghĩa !=.
Hãy dùng nạp chồng toán tử (operator overloading) khi cách này giúp code dễ hiểu hơn, đặc biệt với các kiểu dữ liệu toán học như vectơ, số phức hoặc tiền tệ. Tránh dùng cách này khi ý nghĩa của phép toán không rõ ràng ngay lập tức.
Thử thách
DễHãy xây dựng một lớp Fraction để thao tác với phân số một cách tự nhiên nhờ nạp chồng toán tử. Thay vì gọi các phương thức như fraction1.Add(fraction2), bạn có thể viết biểu thức trực quan fraction1 + fraction2.
Bạn sẽ sắp xếp code trong hai tệp:
Fraction.cs: Tạo lớpFractiontrong không gian tênMathđể biểu diễn phân số với tử số và mẫu số. Phân số của bạn cần có:- Hai thuộc tính số nguyên chỉ đọc:
NumeratorvàDenominator - Một hàm khởi tạo nhận tử số và mẫu số theo thứ tự đó
- Toán tử
+được nạp chồng để cộng hai phân số theo công thức:(a/b) + (c/d) = (a*d + c*b) / (b*d) - Toán tử
*được nạp chồng để nhân hai phân số:(a/b) * (c/d) = (a*c) / (b*d) - Toán tử
==được nạp chồng, trả vềtruenếu hai phân số bằng nhau (so sánh bằng phép nhân chéo:a*d == c*b) - Toán tử
!=được nạp chồng (bắt buộc khi bạn nạp chồng==), trả về kết quả ngược với== - Một phương thức
GetDisplay()trả về phân số dưới dạng chuỗi theo định dạng"{Numerator}/{Denominator}"
- Hai thuộc tính số nguyên chỉ đọc:
Program.cs: Trong tệp chính, tạo hai phân số bằng các giá trị đầu vào. Minh họa các toán tử được nạp chồng bằng cách cộng, nhân và kiểm tra xem hai phân số có bằng nhau không. In kết quả để cho thấy các toán tử hoạt động tự nhiên như thế nào.
Bạn sẽ nhận bốn giá trị đầu vào:
- Tử số của phân số thứ nhất (số nguyên)
- Mẫu số của phân số thứ nhất (số nguyên)
- Tử số của phân số thứ hai (số nguyên)
- Mẫu số của phân số thứ hai (số nguyên)
In kết quả theo định dạng sau:
Fraction 1: {GetDisplay()}
Fraction 2: {GetDisplay()}
Sum: {GetDisplay()}
Product: {GetDisplay()}
Are equal: {True/False}Ví dụ, nếu đầu vào là 1, 2, 2 và 4, kết quả sẽ là:
Fraction 1: 1/2
Fraction 2: 2/4
Sum: 8/8
Product: 2/8
Are equal: TrueLưu ý rằng 1/2 và 2/4 được nhận biết là bằng nhau vì 1*4 == 2*2. Tổng và tích được tính mà không rút gọn, điều đó không sao trong thử thách này. Điều thú vị là giờ đây bạn có thể thao tác với phân số một cách tự nhiên bằng các toán tử quen thuộc!
Tự làm thử
using System;
using Math;
class Program
{
public static void Main(string[] args)
{
// Đọc đầu vào cho phân số thứ nhất
int num1 = Convert.ToInt32(Console.ReadLine());
int den1 = Convert.ToInt32(Console.ReadLine());
// Đọc đầu vào cho phân số thứ hai
int num2 = Convert.ToInt32(Console.ReadLine());
int den2 = Convert.ToInt32(Console.ReadLine());
// TODO: Tạo hai đối tượng Fraction từ các giá trị đầu vào
// TODO: Dùng toán tử + đã nạp chồng để cộng các phân số
// TODO: Dùng toán tử * đã nạp chồng để nhân các phân số
// TODO: Dùng toán tử == đã nạp chồng để kiểm tra hai phân số có bằng nhau không
// TODO: In đầu ra theo định dạng yêu cầu:
// Phân số 1: {GetDisplay()}
// Phân số 2: {GetDisplay()}
// Tổng: {GetDisplay()}
// Tích: {GetDisplay()}
// Có bằng nhau không: {True/False}
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Các khái niệm cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiKhông gian tên và chỉ thịGiới thiệu về lớp và đối tượngTừ khóa thisPhương thức và tham sốTrường và thuộc tínhHàm khởi tạoKhởi tạo đối tượngÔn tập: Máy tính đơn giản4Kế thừa
Cú pháp kế thừa cơ bản (:)Từ khóa 'base'Từ khóa virtual và overrideLớp sealedLớp cơ sở 'object'Ôn tập: hệ phân cấp nhân viên7Tính năng nâng cao
Nạp chồng toán tửBộ lập chỉ mục (this[])Ghi đè ToString()Phương thức mở rộngÔn tập: Danh sách tùy chỉnh10Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếSingleton an toàn với luồngMẫu FactoryMẫu Observer (sự kiện)Mẫu Strategy2Thuộc tính và thành viên tĩnh
Thuộc tính tự độngThuộc tính chỉ đọc hoặc chỉ ghiTrường và phương thức tĩnhLớp tĩnhThành viên có thân biểu thức5Đa hình và interface
Đa hình lúc biên dịch và lúc chạyInterface và lớp trừu tượngNhiều interfaceInterface tường minhUpcasting và downcastingÔn tập: bộ tính toán hình học8Các khái niệm OOP nâng cao
Ưu tiên composition hơn kế thừaGenerics (lớp và phương thức)Delegate và sự kiệnAttribute và reflectionGiao diện IDisposable và câu lệnh usingKiến thức cơ bản về dependency injection11Các mẫu thiết kế phần 2
Mẫu lệnhMẫu bộ chuyển đổiMẫu trang tríMẫu phương thức khuôn mẫuMẫu trạng tháiMẫu tổ hợpTự luyện tập: Trình biên dịch C# online