2016-11-24 11 views
7

Tôi đã tạo cấu trúc tùy chỉnh để biểu thị số tiền. Về cơ bản, nó là một trình bao bọc xung quanh decimal. Nó có toán tử chuyển đổi ẩn để truyền trở lại decimal.Tại sao Assert.AreEqual trên cấu trúc tùy chỉnh với toán tử chuyển đổi ngầm không thành công?

Trong thử nghiệm đơn vị của mình, tôi khẳng định rằng Số tiền bằng với giá trị thập phân ban đầu, nhưng thử nghiệm không thành công.

[TestMethod] 
public void AmountAndDecimal_AreEqual() 
{ 
    Amount amount = 1.5M; 

    Assert.AreEqual(1.5M, amount); 
} 

Khi tôi sử dụng một int dù (mà tôi đã không tạo ra một nhà điều hành chuyển đổi), các thử nghiệm không thành công.

[TestMethod] 
public void AmountAndInt_AreEqual() 
{ 
    Amount amount = 1; 

    Assert.AreEqual(1, amount); 
} 

Khi tôi di AreEqual, nó cho thấy rằng người đầu tiên giải quyết để

public static void AreEqual(object expected, object actual); 

và điều thứ hai dẫn đến

public static void AreEqual<T>(T expected, T actual); 

Dường như giá trị int1 là ngầm truyền tới số Amount, trong khi giá trị decimal1.5M thì không.

Tôi không hiểu tại sao điều này xảy ra. Tôi đã mong đợi điều ngược lại. Thử nghiệm đơn vị đầu tiên sẽ có thể truyền decimal đến Amount.

Khi tôi thêm một diễn viên tiềm ẩn vào int (điều này sẽ không có ý nghĩa), kiểm tra đơn vị thứ hai cũng không thành công. Vì vậy, thêm một nhà điều hành cast tiềm ẩn phá vỡ các bài kiểm tra đơn vị.

Tôi có hai câu hỏi:

  1. lời giải thích cho hành vi này là gì?
  2. Làm cách nào để sửa cấu trúc Amount để cả hai thử nghiệm đều thành công?

(Tôi biết tôi có thể thay đổi thử nghiệm để làm một chuyển đổi rõ ràng, nhưng nếu tôi không hoàn toàn có, tôi sẽ không)

struct Số tiền của tôi (chỉ là một thực hiện tối thiểu để hiển thị sự cố)

public struct Amount 
{ 
    private readonly decimal _value; 

    private Amount(decimal value) 
    { 
     _value = value; 
    } 

    public static implicit operator Amount(decimal value) 
    { 
     return new Amount(value); 
    } 

    public static implicit operator decimal(Amount amount) 
    { 
     return amount._value; 
    } 
} 
+1

Cả hai toán tử của bạn đều là 'ngầm định'. Vì vậy, nó có nên là 'AreEqual 'hoặc' AreEqual '? – PetSerAl

+1

@PetSerAl là đúng. nếu bạn sử dụng 'AreEqual ' hoặc 'AreEqual ' nó sẽ vượt qua. – Nkosi

Trả lời

6

Điều tồi tệ xảy ra khi bạn có thể chuyển đổi implicit ly theo cả hai hướng, và đây là một ví dụ.

Do chuyển đổi tiềm ẩn trình biên dịch có thể chọn Assert.AreEqual<decimal>(1.5M, amount);Assert.AreEqual<Amount>(1.5M, amount); với giá trị tương đương. *

Vì họ đang bình đẳng, không quá tải sẽ được chọn bởi suy luận.

Vì không có quá tải để chọn theo suy luận, không đưa nó vào danh sách để chọn kết quả phù hợp nhất và chỉ có sẵn biểu mẫu (object, object). Vì vậy, đó là một trong những lựa chọn.

Với Assert.AreEqual(1, amount) sau đó kể từ khi có một chuyển đổi ngầm từ int để Amount (thông qua ngầm int-> thập phân) nhưng không có chuyển đổi ngầm từ Amount để int trình biên dịch cho rằng "rõ ràng là họ có nghĩa là Assert.AreEqual<Amount>() here" †, và vì vậy nó là được chọn.

Bạn có thể chọn một cách rõ ràng một tình trạng quá tải với Assert.AreEqual<Amount>() hoặc Assert.AreEqual<decimal>() nhưng bạn có lẽ tốt hơn làm cho một trong những chuyển đổi của bạn "thu hẹp" từ đó có được explicit nếu có thể vì tính năng này của struct của bạn sẽ làm tổn thương bạn một lần nữa . (Hoan hô cho các bài kiểm tra đơn vị tìm lỗi).


* Một sự lựa chọn quá tải có giá trị là để chọn Assert.AreEqual<object>, nhưng nó không bao giờ chọn bởi suy luận vì:

  1. Cả quá tải từ chối được coi là tốt hơn.
  2. Nó luôn bị đánh bại bởi hình thức không chung chung mất object.

Vì vậy, nó chỉ có thể được gọi bằng cách bao gồm <object> trong mã.

† Trình biên dịch xử lý mọi thứ được cho là rõ ràng hoặc có nghĩa là hoàn toàn không thể hiểu được. Có những người như thế.

+0

Cảm ơn câu trả lời của bạn. Trường hợp của tôi, câu lệnh 'implicit' được quay trở lại' thập phân' không thực sự cần thiết, vì vậy tôi đã thay đổi nó thành 'tường minh'. Tôi tự hỏi, làm thế nào tôi có thể đã phát hiện ra một mình tại sao AreEqual (đối tượng, đối tượng) đang được gọi. Không có lỗi trình biên dịch, nhưng nó chỉ là không làm những gì tôi mong đợi. Có lời khuyên nào không? Bạn bằng cách nào đó có thể thấy một số loại cây quyết định cho trình biên dịch? – comecme

+1

@comecme nếu tôi bị nhầm lẫn bởi một lựa chọn quá tải, trước tiên tôi viết một số phương thức khớp với chữ ký (vì vậy ở đây tôi đã tạo ra một 'AssertAreEqual (đối tượng x, đối tượng y)' và 'AssertAreEqual (T x, T y) '. Sau đó xác nhận rằng tôi nhận được cùng một hành vi (dạng đối tượng được lấy), sau đó xóa cái đã chọn xem điều gì xảy ra: 'CS0411 Các đối số kiểu cho phương thức' AssertAreEqual (T, T) 'không thể suy ra từ Hãy thử xác định các loại đối số một cách rõ ràng.' Tự hỏi tại sao họ không thể suy ra sẽ là một đầu mối lớn Bây giờ khi tôi đi đến một cách rõ ràng tôi đã đặt câu hỏi… –

+0

... tôi nên nói rõ ràng ' 'hoặc rõ ràng nói ' '? A ha! Tôi không biết, vậy làm thế nào trình biên dịch có thể vấn đề ID'd. Vấn đề lớn là mặc dù tôi là một cặp mắt mới nhìn vào mã của bạn mà không có một kế hoạch cho nó trong tâm trí w mà làm cho tinh thần cuối cùng nhảy dễ dàng hơn để thực hiện. Khi đó là một trong những mã riêng của mình có một kế hoạch trong tâm trí và nó có thể khó khăn hơn để xem lý do tại sao những gì là rõ ràng cho người đã thực hiện kế hoạch đó không phải là trình biên dịch. Điều duy nhất để làm là đi và uống một tách cà phê và đi bộ về và trở lại với một cái nhìn tươi mới hơn. –

Các vấn đề liên quan