2011-07-27 13 views
11

Tôi đang thử nghiệm việc áp dụng các Hợp đồng Mã cho mã của mình và tôi đã gặp phải một vấn đề rắc rối. Mã này được thất bại trong việc đáp ứng các hợp đồng nhưng trừ khi tôi đang thực sự dày tôi mong chờ nó để có thể dễ dàng phân tích rằng id phải có giá trị tại thời điểm trả lạiCác hợp đồng mã không phát hiện mối quan hệ rõ ràng giữa Nullable <T> .HasValue và null?

if (id == null) 
    throw new InvalidOperationException(string.Format("{0} '{1}' does not yet have an identity", typeof(T).Name, entity)); 

return id.Value; 

Code Contracts error: requires unproven: HasValue

+0

Bạn đã thử 'id.HasValue'!? –

+3

Định nghĩa của trường 'id' là gì? Có phải nó là sự hiểu biết 'readonly'? – Sven

+1

Phát hiện trên Sven! - tốt đẹp bit của gỡ lỗi tâm linh :) (xem bên dưới) – Wheelie

Trả lời

6

Tôi đã đến cuối hành vi này và nó không phải là lỗi của Mã hợp đồng.

tôi mở lắp ráp tạo ra trong ILSpy và đây là mã được sản xuất:

public Guid Id 
{ 
    get 
    { 
     Guid? guid = this.id; 
     if (!guid.HasValue) 
     { 
      throw new InvalidOperationException(); 
     } 
     guid = this.id; 
     return guid.Value; 
    } 
} 

Các ví dụ biến id đã được sao chép vào một biến cục bộ và biến cục bộ này đang được thiết lập lại trở về giá trị ban đầu của nó sau khối điều kiện. Bây giờ nó trở nên rõ ràng lý do tại sao Hợp đồng Mã đang hiển thị một lỗi vi phạm hợp đồng nhưng nó vẫn còn lại cho tôi nhầm lẫn lý do tại sao mã đã được viết lại trong hình thức này. Tôi đã thử nghiệm nhiều hơn một chút và lấy các Hợp đồng Mã ra khỏi dự án hoàn toàn và nó trở nên rõ ràng rằng đây là hành vi trình biên dịch C# tiêu chuẩn, nhưng tại sao?

Bí mật có vẻ là do một chi tiết nhỏ mà tôi vô tình bỏ qua khỏi câu hỏi ban đầu của mình. Biến thể hiện id được khai báo là readonly và điều này dường như có trách nhiệm khiến trình biên dịch thêm biến số guid tạm thời.

Tôi phải thừa nhận tôi vẫn còn lúng túng do tại sao các trình biên dịch cảm thấy nó cần phải làm điều này để đảm bảo sự bảo đảm về tính bất biến cho id nhưng tôi sẽ tiếp tục đào ...

+4

Nếu bạn đọc thông số ngôn ngữ C#, bạn sẽ tìm thấy một điều có nội dung 'readonly' không phải là biến. Giống như các thuộc tính, chúng được coi là các giá trị, mà cho một loại giá trị có nghĩa là tất cả các hoạt động trên chúng phải được thực hiện trên một bản sao. Vì vậy, giống như khi bạn truy cập thuộc tính của một loại giá trị, mỗi lần bạn truy cập vào trường 'readonly' của một loại giá trị, nó tạo ra một bản sao. Thực tế là tôi biết đó là những gì khiến tôi suy đoán rằng đó là vấn đề của bạn dựa trên mã được giải mã. – Sven

+0

Cảm ơn lời giải thích tuyệt vời. Điều đó giải thích bí ẩn! – Wheelie

+0

Chỉ cần nhìn nó lên, các bit có liên quan nằm trong §7.6.4 của đặc tả ngôn ngữ C#. – Sven

1

Bạn có thể thử sao chép trường này sang giá trị cục bộ và viết các câu lệnh theo giá trị cục bộ đó. Trình tục ngữ có thể bảo thủ về các trường, vì có thể một cuộc gọi có thể làm thay đổi giá trị trường.

+0

Dựa trên các vấn đề được phát hiện, tôi tin rằng điều này sẽ làm việc. Bằng cách tạo bản sao một cách rõ ràng, nó chỉ nên tạo bản sao một lần, chứ không phải hai lần (như trong trường hợp chỉ đọc ngầm định.) –

0

của nó không nhìn thấy nếu bạn ném séc như một phần của hợp đồng. Hãy thử điều này thay vì:

if (id == null)  
    throw new InvalidOperationException(string.Format("{0} '{1}' does not yet have an identity", typeof(T).Name, entity)); 

Contract.EndContractBlock(); 

http://msdn.microsoft.com/en-us/library/system.diagnostics.contracts.contract.endcontractblock.aspx

+0

Đây là những gì tôi nghĩ lúc đầu, nhưng 'id' là một trường ở đây, vì vậy có thể không thích hợp để thêm yêu cầu (khách hàng của mã có thể không thể trực tiếp đảm bảo rằng yêu cầu có thể được đáp ứng.Nó có thể có ý nghĩa cho điều này là một bất biến lớp (nếu 'id' nên luôn luôn được thiết lập), nhưng nếu không nó hoàn toàn là một kiểm tra nhà nước mà không nên có yêu cầu người gọi. –

+0

Vấn đề là nếu bạn ném IS vào thực tế một hợp đồng, cho dù khách hàng của bạn có ảnh hưởng đến kết quả của séc hay không. Điều duy nhất thêm EndContractBlock sẽ làm là để cho CC nhận được trên thực tế là nếu bạn ném sau đó là một hợp đồng, mà nó hiện không biết. – Andy

+0

đó là sự thật. Nó có thể là một gợi ý tốt để đóng gói kiểm tra trạng thái này trong một thuộc tính có thể nhìn thấy để khách hàng có một cách đơn giản để biết liệu nó đã hoàn thành kết thúc hợp đồng bằng cách đặt đối tượng ở trạng thái đúng cho cuộc gọi. –

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