Toán tử quá tải C++

Nạp chồng toán tử (toán tử nạp chồng) trong C#

Đăng bởi

Mai Chi

-

09/09/2019

Facebook

Twitter

Pinterest

liên kết

ReddIt

Hướng dẫn tự học lập trình C# toàn tập Nạp chồng toán tử (nạp chồng toán tử) trong C#

Nạp chồng toán tử (toán tử nạp chồng) trong C# có khả năng định nghĩa lại hoạt động của một số toán tử để có thể áp dụng nó với các đối tượng của lớp bạn định nghĩa. Nếu bạn đã biết C++, bạn sẽ rất nhanh chóng bắt được chồng toán tử trong C#. Tuy nhiên, nếu bạn xuất phát từ Java hoặc Visual Basic, đây có thể là vấn đề mới

Bài học này sẽ giúp bạn nắm bắt được ý nghĩa của việc nạp chồng toán tử và cách thực hiện trong C#

NỘI DUNG CỦA BẢNG

Nạp tiền điện tử trong C#

Bộ sưu tập các thuật toán

Đối với các kiểu dữ liệu, C# định nghĩa sẵn một số phép toán như phép toán học, phép toán so sánh, phép toán tăng giảm. Đối với kiểu chuỗi, như chúng ta đã biết, được phép tính toán cộng gộp sẵn có

Tuy nhiên, các kiểu dữ liệu (lớp) do người dùng định nghĩa lại không thể sử dụng ngay các phép toán đó được

Ví dụ, nếu người dùng định nghĩa kiểu số phức, phép toán cơ bản trên kiểu số phức hợp lại không thể thực hiện được ngay, mặc dù về mặt toán học thì phép tính toán đối với kiểu số không có gì khác biệt với kiểu số

Để giải quyết các vấn đề tương tự, C# cho phép nạp chồng toán tử, tức là cho phép định nghĩa lại các phép toán đã có với kiểu dữ liệu do người dùng xây dựng xây dựng

Nạp chồng phương thức (nạp chồng phương thức) cùng với nạp chồng toán tử (nạp chồng toán tử) là hai đối tượng thuộc về nguyên lý đa hình tĩnh (đa hình tĩnh)

Cách nạp chồng toán tử trong C#

Hãy cùng thực hiện và phân tích ví dụ sau để hiểu cách nạp chồng toán. Chú ý xem xét cú pháp nạp đối với mỗi toán tử

using System;
namespace P01_OperatorOverload
{
    /// <summary>
    /// lớp biểu diễn hình hộp
    /// </summary>
    internal class Box
    {
        public double Length { get; set; }
        public double Breadth { get; set; }
        public double Height { get; set; }
        public Box() { }
        public Box(double length, double breadth, double height)
        {
            Length = length;
            Breadth = breadth;
            Height = height;
        }
        /// <summary>
        /// tính thể tích khối hộp
        /// </summary>
        public double Volume => Length * Breadth * Height;
        // nạp chồng phép cộng
        public static Box operator +(Box b, Box c)
        {
            Box box = new Box
            {
                Length = b.Length + c.Length,
                Breadth = b.Breadth + c.Breadth,
                Height = b.Height + c.Height
            };
            return box;
        }
        // nạp chồng phép so sánh bằng
        public static bool operator ==(Box lhs, Box rhs)
        {
            bool status = false;
            if (lhs.Length == rhs.Length && lhs.Height == rhs.Height
               && lhs.Breadth == rhs.Breadth)
            {
                status = true;
            }
            return status;
        }
        // nạp chồng phép so sánh khác
        public static bool operator !=(Box lhs, Box rhs)
        {
            bool status = false;
            if (lhs.Length != rhs.Length || lhs.Height != rhs.Height ||
               lhs.Breadth != rhs.Breadth)
            {
                status = true;
            }
            return status;
        }
        // nạp chồng phép so sánh nhỏ hơn
        public static bool operator <(Box lhs, Box rhs)
        {
            bool status = false;
            if (lhs.Length < rhs.Length && lhs.Height < rhs.Height
               && lhs.Breadth < rhs.Breadth)
            {
                status = true;
            }
            return status;
        }
        // nạp chồng phép so sánh lớn hơn
        public static bool operator >(Box lhs, Box rhs)
        {
            bool status = false;
            if (lhs.Length > rhs.Length && lhs.Height >
               rhs.Height && lhs.Breadth > rhs.Breadth)
            {
                status = true;
            }
            return status;
        }
        public override string ToString()
        {
            return string.Format("({0}, {1}, {2})", Length, Breadth, Height);
        }
    }
    internal class Program
    {
        private static void Main(string[] args)
        {
            Box Box1 = new Box(6, 7, 5);   
            Box Box2 = new Box(12, 13, 10);   
            Box Box3 = new Box();   
            Box Box4 = new Box();
            /* phép cộng hai hình hộp cho ra hình hộp khác có kích thước 
             * bằng tổng kích thước của hai hộp */
            Box3 = Box1 + Box2;
            Console.WriteLine("Box 3: {0}", Box3.ToString());
            Console.WriteLine("Volume of Box3 : {0}", Box3.Volume);
            // so sánh hai hình hộp
            if (Box1 > Box2)
                Console.WriteLine("Box1 lớn hơn Box2");
            else
                Console.WriteLine("Box1 không lớn hơn Box2");
            if (Box3 == Box4)
                Console.WriteLine("Box3 bằng Box4");
            else
                Console.WriteLine("Box3 không bằng Box4");
            Console.ReadKey();
        }
    }
}

Trong ví dụ trên chúng ta đã thực hiện nạp chồng phép cộng (+), phép so sánh (bằng ==, khác. =, lớn hơn >, nhỏ hơn <)

Cú pháp báo cáo này được tổng hợp lại dưới đây

public static Box operator +(Box b, Box c) {...}
public static bool operator ==(Box lhs, Box rhs) {...} 
public static bool operator !=(Box lhs, Box rhs) {...} 
public static bool operator <(Box lhs, Box rhs) {...} 
public static bool operator >(Box lhs, Box rhs) {...} 

Nếu để ý kỹ hơn nữa chúng ta thấy, đây đều là các phép toán nhị phân. Cách nạp các phép toán này có cùng một cú pháp

Mỗi loại phép toán sẽ có cách tải riêng. Tuy nhiên, cú pháp chung là

public static <return_type> operator <operator>(<parameters>) { .. }

Các toán tử có thể nạp chồng: +, -, !, ~, ++, –, +, -, *, /, %, ==, !=, <, >, <=, >=

Ngoài ra phép toán indexer còn là một phép toán có thể nạp chồng

Một số lưu ý khi tải thực hiện tài khoản điện tử

Các phép toán chia làm ba loại. đơn hạng (chỉ cần một toán hạng, như phép toán tăng ++, phép toán giảm –), nhị phân (cần hai toán hạng, như phép toán +,-,*,/), ternary (cần ba toán hạng, như phép . Do đó, khi nạp bất kỳ khoản thanh toán cho phép nào thì phải cung cấp đủ lượng tham số phù hợp. Ví dụ, khi nạp chồng phép toán nhị phân (như +, -) thì phải cấp 2 tham số như đã làm ở trên

Phép toán tăng giảm (++, –) thuộc loại đơn nguyên nên trong danh sách tham số chỉ cần 1 tham số. Các phép toán này cũng không có giới hạn gì khi nạp chồng. With the example with Layer Box on

public static Box operator ++ (Box b)
{
    return new Box(b.Length++, b.Breadth++, b.Height++);
}

Các phép toán học (+, – *, /, %) không đặt ra giới hạn nào khi tải chồng. Bạn chỉ cần tuân thủ cú pháp đúng như trên đã được

Bạn thậm chí có thể tải lên cùng một phép toán nhiều lần. Ví dụ, bạn hoàn toàn có thể nạp chồng phép + một lần nữa như sau

public static Box operator +(Box b, double size)
{
    return new Box(b.Length += size, b.Breadth += size, b.Height + size);
}

Ở đây bạn đã tải xuống hộp cộng cho phép với một số thực. Điều kiện cho phép nạp chồng nhiều lần là danh sách tham số của mỗi lần nạp chồng phải khác nhau

Đối với các phép so sánh toán học, bạn phải thực hiện nạp cả cặp. Nghĩa là, nếu tải cả phép so sánh bằng == thì đồng thời phải tải cả phép so sánh khác. =;

Các phép gán (+=, -=, v. v. ) không cho phép tải trực tiếp. Tuy nhiên, nếu bạn đã cho phép tải +,-, v. v. thì các phép toán này tự nhiên sẽ được tải lên. Ví dụ, nếu bạn đã nạp chồng phép cộng Box với 1 số như trên thì hoàn toàn có thể gọi lệnh

var Box5 = Box4 += 5; // phép cộng gán với số

Bộ chỉ mục toán học riêng cho phép có cách thực hiện nạp chồng riêng dưới đây

Nạp chỉ mục toán học cấp phép trong C#

Bộ lập chỉ mục là một phép toán giúp mã máy khách sử dụng đối tượng tương tự như khi sử dụng mảng. Indexer thường được sử dụng với các kiểu dữ liệu chứa trong nó một tập hợp dữ liệu (bộ sưu tập hoặc mảng). Trình lập chỉ mục giúp đơn giản hóa việc sử dụng ở mã máy khách

Trước khi xem cú pháp nạp chỉ mục toán tử, hãy cùng thực hiện ví dụ sau

namespace P02_IndexerOverload
{
    using static System.Console;
    class Program
    {
        static void Main(string[] args)
        {
            var vector1 = new Vector(1, 2, 3);
            WriteLine($"vector 1: {vector1}");
            ReadLine();
        }
    }
    class Vector
    {
        private double[] _components;
        public Vector(int dimension)
        {
            _components = new double[dimension];
        }
        public Vector(params double[] components)
        {
            _components = components;
        }
        public override string ToString()
        {
            return $"({string.Join(", ", _components)})";
        }
    }
}

Ví dụ này xây dựng một lớp Vector đơn giản dành cho vector n-chiều. Cả vector được lưu trong một _components mảng riêng (mỗi phần tử của mảng là kích thước một chiều của vector). Lớp này có 2 quá tải cho hàm tạo, một cái nhận chiều làm tham số, một cái nhận mảng double làm tham số

Bạn có muốn truy xuất giá trị từng chiều của vectơ này như truy xuất phần tử của mảng không? . v. , trong đó 0, 1, chỉ là chiều

Cú pháp load indexer

Cú pháp cấp phép lập chỉ mục toán học cho thuộc tính gần giống của lớp, trong đó phải có ít nhất một trong hai phương thức get/set, dùng để trả lại giá trị và gán giá trị. Khác biệt duy nhất ở vị trí bộ lập chỉ mục bắt buộc sử dụng từ khóa 

public static Box operator +(Box b, Box c) {...}
public static bool operator ==(Box lhs, Box rhs) {...} 
public static bool operator !=(Box lhs, Box rhs) {...} 
public static bool operator <(Box lhs, Box rhs) {...} 
public static bool operator >(Box lhs, Box rhs) {...} 
1 với cặp dấu ngoặc kép. Key shift must set in the dấu ngoặc kép

________số 8_______

in which

  1. public static Box operator +(Box b, Box c) {...}
    public static bool operator ==(Box lhs, Box rhs) {...} 
    public static bool operator !=(Box lhs, Box rhs) {...} 
    public static bool operator <(Box lhs, Box rhs) {...} 
    public static bool operator >(Box lhs, Box rhs) {...} 
    2 là kiểu dữ liệu trả về, 
    public static Box operator +(Box b, Box c) {...}
    public static bool operator ==(Box lhs, Box rhs) {...} 
    public static bool operator !=(Box lhs, Box rhs) {...} 
    public static bool operator <(Box lhs, Box rhs) {...} 
    public static bool operator >(Box lhs, Box rhs) {...} 
    3 là kiểu dữ liệu của khóa;
  2. số lượng khóa có thể nhiều hơn 1;
  3. type of key could be any type of data type (not most essential a number or string);
  4. get and set active method same as đối tượng thuộc tính

Nạp chồng chỉ mục toán tử cho lớp Vector

Thêm đoạn mã sau vào lớp Vector

public double this[int index]
{
    get => (index < _components.Length) ? _components[index] : double.NaN;
    set { if (index < _components.Length) _components[index] = value; }
}

Cấu trúc kiến ​​trúc này trông giống như một tài sản đầy đủ, ngoại trừ tên gọi

public static Box operator +(Box b, Box c) {...}
public static bool operator ==(Box lhs, Box rhs) {...} 
public static bool operator !=(Box lhs, Box rhs) {...} 
public static bool operator <(Box lhs, Box rhs) {...} 
public static bool operator >(Box lhs, Box rhs) {...} 
4. Đoạn mã này đã thực hiện tải bộ chỉ mục toán tử cho lớp Vector

Hoạt động logic của getter rất đơn giản. Nếu chỉ mục nhỏ hơn số phần tử của mảng thì trả về giá trị tương ứng với chỉ mục, nếu không thì trả về giá trị NaN (Không phải là số). Setter chỉ gán giá trị cho phần tử tương ứng của mảng

Nếu phương thức get/set chỉ chứa một lệnh duy nhất thì có thể sử dụng cú pháp “body biểu thức” để rút gọn. Trong mã của getter ở trên chúng ta đã sử dụng cấu trúc này

Nội dung biểu thức là một lối viết xuất hiện từ C# 6. if body of the method only contain a command duy nhất có thể sử dụng cấu trúc như sau để viết
Tên_phương_thức() => command;
Từ C# 7 có thể sử dụng phần thân biểu thức cho cả phương thức nhận và đặt thuộc tính

Bây giờ bạn có thể truy xuất từng chiều của vector như sau

static void Main(string[] args)
{
    var vector1 = new Vector(1, 2, 3);
    WriteLine($"vector 1: {vector1}");
    var x = vector1[0];
    var y = vector1[1];
    var z = vector1[2];
    WriteLine($"Vector components: x = {x}, y = {y}, z = {z}");
    vector1[2] = 30;
    vector1[1] = 20;
    vector1[0] = 10;
    WriteLine($"vector 1: {vector1}");
    ReadLine();
}

Dưới đây là mã đầy đủ của ví dụ trên

public static Box operator +(Box b, Box c) {...}
public static bool operator ==(Box lhs, Box rhs) {...} 
public static bool operator !=(Box lhs, Box rhs) {...} 
public static bool operator <(Box lhs, Box rhs) {...} 
public static bool operator >(Box lhs, Box rhs) {...} 
0

Kết luận

Bài học này đã hướng dẫn các bạn làm quen với việc nạp chồng toán tử cho lớp trong C#. Bạn có thể thấy, thực hiện nạp chồng toán tử trong C# rất đơn giản