Ví dụ về quá tải toán tử, có nghĩa là [đóng]


12

Trong khi tôi học C #, tôi thấy rằng, C # hỗ trợ quá tải toán tử. Tôi có vấn đề với ví dụ tốt đó:

  1. Có ý nghĩa (ví dụ: thêm lớp có tên cừu và bò)
  2. Không phải là một ví dụ về nối hai chuỗi

Ví dụ từ Thư viện lớp cơ sở được chào đón.


10
Hãy xác định "ý nghĩa", làm ơn! Nghiêm túc mà nói, các cuộc tranh luận cay đắng và vô tư về chính xác điểm này cho thấy có sự bất đồng lớn về chính xác điều này. Nhiều nhà chức trách từ chối các nhà khai thác quá tải vì họ có thể được thực hiện để làm những điều hoàn toàn bất ngờ. Những người khác trả lời rằng tên phương thức cũng có thể được chọn là hoàn toàn không trực quan, nhưng đó không phải là lý do để từ chối các khối mã được đặt tên! Bạn gần như chắc chắn sẽ không nhận được bất kỳ ví dụ nào thường được coi là hợp lý. Các ví dụ có vẻ hợp lý với bạn - có thể.
Kilian Foth

Hoàn toàn đồng ý với @KilianFoth. Cuối cùng, chương trình biên dịch, có ý nghĩa với trình biên dịch. Nhưng nếu quá tải ==để làm phép nhân, nó có ý nghĩa với tôi nhưng có thể không có ý nghĩa với người khác! Đây có phải là câu hỏi về tính hợp pháp của ngôn ngữ lập trình cơ sở nào hay chúng ta đang nói về 'thực hành tốt nhất về mã hóa'?
Dipan Mehta

Câu trả lời:


27

Các ví dụ rõ ràng về quá tải toán tử thích hợp là bất kỳ lớp nào hoạt động giống như cách các số hoạt động. Vì vậy, bigint lớp (như Jalayn gợi ý), số phức hoặc ma trận lớp (như Superbest gợi ý) tất cả đều có các hoạt động tương tự mà con số bình thường đã rất bản đồ thực sự tốt vào khai thác toán học, trong khi thời gian hoạt động (theo đề nghị của svick ) bản đồ độc đáo vào một tập hợp con của những hoạt động đó.

Tóm tắt hơn, các toán tử có thể được sử dụng khi thực hiện tập hợp như các phép toán, vì vậy operator+có thể là một phép hợp , operator-có thể là phần bù, v.v ... Điều này bắt đầu kéo dài mô hình, đặc biệt là nếu bạn sử dụng toán tử cộng hoặc nhân cho một thao tác không phải là ' t giao hoán , như bạn có thể mong đợi chúng được.

Bản thân C # có một ví dụ tuyệt vời về quá tải toán tử không số . Nó sử dụng +=-=để cộng và trừ các đại biểu , tức là đăng ký và hủy đăng ký chúng. Điều này hoạt động tốt bởi vì +=và các -=toán tử hoạt động như bạn mong đợi, và điều này dẫn đến mã ngắn gọn hơn nhiều.

Đối với người theo chủ nghĩa thuần túy, một trong những vấn đề với +toán tử chuỗi là nó không giao hoán. "a"+"b"không giống như "b"+"a". Chúng tôi hiểu ngoại lệ này cho các chuỗi vì nó rất phổ biến, nhưng làm thế nào chúng ta có thể biết nếu sử dụng operator+trên các loại khác sẽ có giao hoán hay không? Hầu hết mọi người sẽ cho rằng nó là như vậy, trừ khi đối tượng giống như chuỗi , nhưng bạn không bao giờ thực sự biết mọi người sẽ giả định gì.

Cũng như các chuỗi, các phần tử của ma trận cũng khá nổi tiếng. Rõ ràng đó Matrix operator* (double, Matrix)là một phép nhân vô hướng, trong khi đó Matrix operator* (Matrix, Matrix)sẽ là một phép nhân ma trận (ví dụ như một ma trận của phép nhân sản phẩm chấm).

Tương tự như vậy, việc sử dụng các toán tử với các đại biểu rõ ràng đã bị loại bỏ khỏi toán học đến mức bạn khó có thể mắc phải những sai lầm đó.

Tình cờ, tại hội nghị ACCU 2011 , Roger Orr & Steve Love đã trình bày một phiên về Một số đối tượng bình đẳng hơn những đối tượng khác - một cái nhìn về nhiều ý nghĩa của sự bình đẳng, giá trị và bản sắc . Các slide của họ có thể tải xuống , như Phụ lục của Richard Harris về sự bình đẳng điểm nổi . Tóm tắt: Hãy thật cẩn thận với operator==, đây là những con rồng!

Quá tải toán tử là một kỹ thuật ngữ nghĩa rất mạnh, nhưng nó dễ sử dụng quá mức. Lý tưởng nhất là bạn chỉ nên sử dụng nó trong các tình huống khi nó rất rõ ràng từ ngữ cảnh tác động của một toán tử quá tải là gì. Trong nhiều cách a.union(b)rõ hơn a+b, và a*bnhiều hơn che khuất hơn a.cartesianProduct(b), đặc biệt là kể từ khi kết quả của một sản phẩm Descartes sẽ là một SetLike<Tuple<T,T>>chứ không phải là một SetLike<T>.

Các vấn đề thực sự với quá tải toán tử xuất hiện khi một lập trình viên giả định một lớp sẽ hành xử theo một cách, nhưng nó thực sự hành xử theo cách khác. Loại xung đột ngữ nghĩa này là những gì tôi cho rằng điều quan trọng là phải cố gắng tránh.


1
Bạn nói rằng các toán tử trên bản đồ ma trận thực sự tốt, nhưng phép nhân ma trận cũng không giao hoán. Ngoài ra các nhà khai thác trên các đại biểu thậm chí còn mạnh mẽ hơn. Bạn có thể làm d1 + d2cho bất kỳ hai đại biểu cùng loại.
Svick

1
@Mark: "Sản phẩm chấm" chỉ được xác định trên các vectơ; nhân hai ma trận được gọi đơn giản là "phép nhân ma trận". Sự khác biệt không chỉ là ngữ nghĩa: sản phẩm chấm trả về vô hướng, trong khi phép nhân ma trận trả về một ma trận (và nhân tiện, không giao hoán) .
BlueRaja - Daniel Pflughoeft

26

Tôi ngạc nhiên không ai đề cập đến một trong những trường hợp thú vị hơn trong BCL: DateTimeTimeSpan. Bạn có thể:

  • cộng hoặc trừ hai TimeSpans để lấy số khácTimeSpan
  • sử dụng trừ unary trên a TimeSpanđể có được phủ địnhTimeSpan
  • trừ hai DateTimes để có được mộtTimeSpan
  • cộng hoặc trừ TimeSpantừ a DateTimeđể lấy cái khácDateTime

Một tập hợp các toán mà có thể có ý nghĩa trên nhiều loại là <, >, <=, >=. Trong BCL, ví dụ Versionthực hiện chúng.


Ví dụ rất thực tế hơn là lý thuyết nhi khoa!
SIslam 8/2/2016

7

Ví dụ đầu tiên tôi nghĩ đến là việc triển khai BigInteger , cho phép bạn làm việc với các số nguyên có chữ ký lớn. Kiểm tra liên kết MSDN để xem có bao nhiêu toán tử đã bị quá tải (nghĩa là có một danh sách lớn và tôi không kiểm tra xem tất cả các toán tử đã bị quá tải hay chưa, nhưng chắc chắn là như vậy)

Ngoài ra, vì tôi cũng làm Java và Java không cho phép các toán tử quá tải, nên viết nó cực kỳ ngọt ngào

BigInteger bi = new BigInteger(0);
bi += 10;

Hơn, trong Java:

BigDecimal bd = new BigDecimal(0);
bd = bd.add(new BigDecimal(10));

5

Tôi rất vui vì tôi đã thấy điều này bởi vì tôi đã bị lừa với Irony và nó có một cách sử dụng quá tải toán tử TUYỆT VỜI. Đây là một mẫu của những gì nó có thể làm.

Vì vậy, Irony là một "Bộ thực thi ngôn ngữ .NET" và là trình tạo trình phân tích cú pháp (tạo trình phân tích cú pháp LALR). Thay vì phải học một cú pháp / ngôn ngữ mới như các trình tạo trình phân tích cú pháp như yacc / lex, bạn viết ngữ pháp bằng C # với sự quá tải của toán tử. Đây là một ngữ pháp BNF đơn giản

// BNF 
Expr := Term | BinExpr
Term := number | ParExpr
ParExpr := "(" + Expr + ")"
BinExpr := number + BinOp + number
BinOp := "+" | "-" | "*" | "/"
number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

Vì vậy, đây là một ngữ pháp nhỏ đơn giản (xin vui lòng xin lỗi nếu có sự không nhất quán vì tôi chỉ học BNF và xây dựng ngữ pháp). Bây giờ hãy xem C #:

  var Expr = new NonTerminal("Expr");
  var Term = new NonTerminal("Term");
  var BinExpr = new NonTerminal("BinExpr");
  var ParExpr = new NonTerminal("ParExpr");
  var BinOp = new NonTerminal("BinOp");
  var Statement = new NonTerminal("Statement");
  var ProgramLine = new NonTerminal("ProgramLine");
  var Program = new NonTerminal("Program", typeof(StatementListNode));
  // BNF Rules - Overloading
  Expr.Rule = Term | BinExpr;
  Term.Rule = number | ParExpr;
  ParExpr.Rule = "(" + Expr + ")";
  BinExpr.Rule = Expr + BinOp + Expr;
  BinOp.Rule = ToTerm("+") | "-" | "*" | "/" | "**";

Như bạn có thể thấy, với toán tử quá tải, viết ngữ pháp bằng C # gần như viết chính xác ngữ pháp trong BNF. Đối với tôi, điều đó không chỉ có ý nghĩa mà còn là một công dụng tuyệt vời của quá tải toán tử.


3

Ví dụ chính là toán tử == / toán tử! =.

Nếu bạn muốn dễ dàng so sánh hai đối tượng với các giá trị dữ liệu thay vì tham chiếu, bạn sẽ muốn quá tải .Equals (and.GetHashCode!) Và có thể muốn thực hiện các toán tử! = Và == để thống nhất.

Tôi chưa bao giờ thấy bất kỳ sự quá tải hoang dã nào của các nhà khai thác khác trong C # (tôi tưởng tượng có những trường hợp cạnh có thể hữu ích mặc dù).



0

Sử dụng tốt quá tải có thể là hiếm, nhưng nó xảy ra.

quá tải toán tử == và toán tử! = hiển thị hai trường phái suy nghĩ: những trường hợp nói điều đó làm cho mọi việc dễ dàng hơn và những người chống lại việc nói điều đó ngăn cản việc so sánh địa chỉ (ví dụ: tôi chỉ vào cùng một vị trí trong bộ nhớ, không chỉ là một bản sao của cùng một vật).

Tôi thấy quá tải toán tử diễn viên sẽ có ích trong các tình huống cụ thể. Ví dụ, tôi đã phải tuần tự hóa / giải tuần tự hóa trong XML một boolean được biểu diễn là 0 hoặc 1. Toán tử chuyển quyền (ẩn hoặc rõ ràng, tôi quên) từ boolean sang int và back đã thực hiện thủ thuật.


4
Nó không ngăn so sánh địa chỉ: Bạn vẫn có thể sử dụng object.ReferenceEquals().
dan04

@ dan04 Rất rất tốt để biết!
MPelletier

Một cách khác để so sánh địa chỉ là bắt buộc sử dụng đối tượng ==bằng cách truyền: (object)foo == (object)barluôn so sánh các tham chiếu. Nhưng tôi thích ReferenceEquals(), như @ dan04 đề cập vì nó rõ ràng hơn những gì nó làm.
Svick

0

Chúng không thuộc danh mục những thứ mà mọi người thường nghĩ đến khi chúng quá tải toán tử, nhưng tôi nghĩ một trong những toán tử quan trọng nhất có thể quá tải là toán tử chuyển đổi .

Toán tử chuyển đổi đặc biệt hữu ích cho các loại giá trị có thể "khử đường" thành loại số hoặc có thể hoạt động như một loại số trong một số ngữ cảnh. Ví dụ: bạn có thể xác định một Idloại đặc biệt đại diện cho một mã định danh nhất định và bạn có thể cung cấp một chuyển đổi ngầm định để intbạn có thể chuyển Idmột phương thức có một int, nhưng một chuyển đổi bùng nổ từ đó intsang Idkhông ai có thể chuyển intvào phương pháp mà Idkhông cần đúc nó trước.

Như một ví dụ bên ngoài C #, ngôn ngữ Python bao gồm nhiều hành vi đặc biệt được triển khai dưới dạng toán tử quá tải. Chúng bao gồm intoán tử để kiểm tra thành viên, ()toán tử để gọi một đối tượng như thể đó là một hàm và lentoán tử để xác định chiều dài hoặc kích thước của một đối tượng.

Và sau đó, bạn có các ngôn ngữ như Haskell, Scala và nhiều ngôn ngữ chức năng khác, trong đó các tên như +chỉ là các hàm thông thường và không phải là toán tử nào cả (và có hỗ trợ ngôn ngữ để sử dụng các hàm ở vị trí trung gian).


0

Các điểm Struct trong System.Drawing namespace sử dụng được quá tải để so sánh hai địa điểm khác nhau sử dụng toán tử quá tải.

 Point locationA = new Point( 50, 50 );
 Point locationB = new Point( 50, 50 );

 if ( locationA == locationB )
    Console.WriteLine( "Their locations are the same" );
 else
    Console.WriteLine( "Their locations  are different" );

Như bạn có thể thấy, việc so sánh tọa độ X và Y của hai vị trí sử dụng quá tải sẽ dễ dàng hơn nhiều.


0

Nếu bạn quen thuộc với vectơ toán học, bạn có thể thấy việc sử dụng quá tải +toán tử. Bạn có thể thêm một vectơ a=[1,3]với b=[2,-1]và nhận c=[3,2].

Quá tải các giá trị bằng (==) cũng có thể hữu ích (mặc dù có thể tốt hơn để thực hiện một equals()phương thức). Để tiếp tục các ví dụ vector:

v1=[1,3]
v2=[1,3]
v1==v2 // True

-2

Hãy tưởng tượng một đoạn mã để vẽ trên một biểu mẫu

{
  Point p = textBox1.Location;
  Size dp = textBox1.Size;

  // Here the + operator has been overloaded by the CLR
  p += dp;  // Now p points to the lower right corner of the textbox.
  ..
}

Một ví dụ phổ biến khác là khi một cấu trúc được sử dụng để giữ thông tin vị trí dưới dạng một vectơ.

public struct Pos
{
    public double x, y, z;
    public double Distance { get { return Math.Sqrt(x * x + y * y + z * z); } }
    public static Pos operator +(Pos A, Pos B)
    {
        return new Pos() { x = A.x + B.x, y = A.y + B.y, z = A.z + B.z };
    }
    public static Pos operator -(Pos A, Pos B)
    {
        return new Pos() { x = A.x - B.x, y = A.y - B.y, z = A.z - B.z };
    }
}

chỉ được sử dụng sau này như

{
    Pos A = new Pos() { x = 4, y = -1, z = 0.5 };
    Pos B = new Pos() { x = 8, y = 2, z = 1.5 };

    double x = (B - A).Distance;
}

4
Bạn thêm vectơ, không vị trí: \ Đây là một ví dụ tốt về khi operator+nên không bị quá tải (bạn có thể thực hiện một điểm trong điều khoản của một véc tơ, nhưng bạn không nên có thể thêm hai điểm)
BlueRaja - Danny Pflughoeft

@ BlueRaja-DannyPflughoeft: Thêm các vị trí để mang lại một vị trí khác không có ý nghĩa gì, nhưng trừ chúng (để tạo ra một vectơ) thì cũng như tính trung bình của chúng. Người ta có thể tính trung bình của p1, p2, p3 và p4 thông qua p1+((p2-p1)+(p3-p1)+(p4-p1))/4, nhưng điều đó có vẻ hơi khó xử.
supercat

1
Trong hình học affine, bạn có thể thực hiện đại số với các điểm và đường thẳng, như cộng, chia tỷ lệ, v.v ... Việc triển khai mặc dù đòi hỏi tọa độ đồng nhất, thường được sử dụng trong đồ họa 3D. Việc thêm hai điểm thực sự dẫn đến kết quả trung bình của họ.
ja72
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.