2009-03-21 31 views
15

Tôi đang gặp sự cố khi cố gắng quá tải toán tử tăng bài đăng trong C#. Sử dụng các số nguyên, chúng tôi nhận được các kết quả sau.Quá tải sau khi tăng quá mức

int n; 

n = 10; 
Console.WriteLine(n); // 10 
Console.WriteLine(n++); // 10 
Console.WriteLine(n); // 11 

n = 10; 
Console.WriteLine(n); // 10 
Console.WriteLine(++n); // 11 
Console.WriteLine(n); // 11 

Nhưng, khi tôi thử sử dụng lớp học, có vẻ như các đối tượng được trao đổi.

class Account 
{ 
    public int Balance { get; set; } 
    public string Name { get; set; } 

    public Account(string name, int balance) 
    { 
     Balance = balance; 
     Name = name; 
    } 

    public override string ToString() 
    { 
     return Name + " " + Balance.ToString(); 
    } 

    public static Account operator ++(Account a) 
    { 
     Account b = new Account("operator ++", a.Balance); 
     a.Balance += 1; 
     return b; 
    } 

    public static void Main() 
    { 
     Account a = new Account("original", 10); 

     Console.WriteLine(a); // "original 10" 

     Account b = a++; 

     Console.WriteLine(b); // "original 11", expected "operator ++ 10" 
     Console.WriteLine(a); // "operator ++ 10", expected "original 11" 
    } 
} 

Debugging ứng dụng, phương pháp điều hành quá tải, trả về đối tượng mới với giá trị cũ (10) và đối tượng đã được thông qua tham khảo, có giá trị mới (11), nhưng cuối cùng các đối tượng là trao đổi. Tại sao chuyện này đang xảy ra?

+0

Bạn không nên thay đổi các câu hỏi sau câu trả lời , nó gây nhầm lẫn cho những người tiếp theo nhìn vào nó. – RedGlyph

+0

có thể trùng lặp của [Sự khác biệt giữa X = X ++; vs X ++ ;?] (http://stackoverflow.com/questions/226002/whats-the-difference-between-xx-vs-x) – nawfal

Trả lời

11

Điều quan trọng là hiểu cách dòng Account b = a++; hoạt động. Với cách mã của bạn được viết, dòng này tương đương với điều này:

Account b = a; 
a++; 

Và đó là thứ tự nó sẽ thực hiện. Vì vậy, hiệu ứng đầu tiên của dòng này là ab cả hai đều là đối tượng gốc a.

Bây giờ phần ++ sẽ được đánh giá. Bên trong phương thức toán tử, chúng ta tăng số Balance của đối tượng gốc. Tại thời điểm này, ab đều trỏ vào bản gốc, với Balance trong số 11 và b sẽ tiếp tục làm như vậy.

Tuy nhiên, bạn đã tạo một đối tượng mới bên trong phương thức toán tử và trả về nó làm đầu ra của toán tử. a giờ đây sẽ được cập nhật để trỏ tới đối tượng mới được tạo.

Vì vậy, a giờ trỏ đến một đối tượng mới, trong khi b tiếp tục trỏ đến bản gốc. Đó là lý do tại sao đầu ra WriteLine xuất hiện hoán đổi.

Khi @MarkusQ chỉ ra, toán tử ++ có nghĩa là thực hiện sửa đổi tại chỗ. Bằng cách tạo ra một đối tượng mới, bạn đang phá vỡ giả định đó. Vận hành quá tải trên các đối tượng là một chủ đề khó khăn, và đây là một ví dụ tuyệt vời về lý do tại sao nó tránh được tốt hơn trong hầu hết các trường hợp.


1 - Chỉ vì độ chính xác của, sự phân công không thực sự xảy ra trước khi tăng khi giao dịch với các nhà khai thác trên các đối tượng, nhưng kết quả cuối cùng là như nhau trong trường hợp này. Trên thực tế, tham chiếu đối tượng ban đầu được sao chép, thao tác được thực hiện trên bản gốc và sau đó tham chiếu đã sao chép được gán cho biến bên trái. Sẽ dễ dàng hơn để giải thích nếu bạn giả vờ rằng bài tập đó xảy ra trước.

gì thực sự xảy ra là điều này:

Account b = a++; 

kết quả trong này, do cách thức operator ++ hoạt động trên các đối tượng:

Account copy = a; 

Account x = new Account("operator ++", a.Balance); 
a.Balance += 1; // original object's Balance is incremented 
a = x; // a now points to the new object, copy still points to the original 

Account b = copy; // b and copy now point at the same, original, object 
+0

Giải thích tốt (Tôi không biết chắc chắn nếu nó đúng nhưng nó có ý nghĩa :-). Không, nghiêm túc, điều này là tốt. +1. – paxdiablo

+0

+1 dành cho người đi bộ. – Brian

+0

Tôi nghĩ rằng nó là ** tốt ** để tạo ra một đối tượng mới bên trong cơ thể phương thức vận hành. Chỉ thay đổi đối tượng mới và không thay đổi đối tượng gốc (được gọi là 'a') mà bạn đã cho. Theo cách đó, nó sẽ hoạt động giống như một cấu trúc. Trình biên dịch C# sẽ thực hiện nhiệm vụ chính xác sau đó, hoặc là trước hoặc sau. –

13

Suy nghĩ đầu tiên của tôi là chỉ ra rằng ngữ nghĩa thông thường của ++ là sửa đổi tại chỗ. Nếu bạn muốn bắt chước bạn muốn viết:

public static Account operator ++(Account a) 
{ 
    a.Balance += 1; 
    return a; 
} 

và không tạo đối tượng mới.

Nhưng sau đó tôi nhận ra rằng bạn đang cố gắng bắt chước tăng bài đăng. Vì vậy, ý nghĩ thứ hai của tôi là "không làm điều đó" - ngữ nghĩa không ánh xạ tốt vào tất cả các đối tượng, vì giá trị được "sử dụng" thực sự là một vị trí lưu trữ có thể thay đổi. Nhưng không ai thích nói "đừng làm điều đó" bởi một người lạ ngẫu nhiên nên tôi sẽ để Microsoft tell you not to do it. Và tôi sợ từ của họ là cuối cùng về những vấn đề như vậy.

P.S. Khi đến lý do tại sao nó đang làm những gì nó làm, bạn đang thực sự ghi đè các nhà điều hành preincrement, và sau đó sử dụng nó như thể nó đã được các nhà điều hành postincrement.

+1

Trang không có sẵn công khai và không tồn tại :-) Chỉ may mắn của tôi. .. Bạn có nhớ tiêu đề, hoặc những gì để tìm? – greenoldman

1

Bạn nên luôn trả lại giá trị đã thay đổi. C# sử dụng giá trị này làm giá trị mới và trả về giá trị cũ hoặc mới khi thích hợp cho toán tử.