2011-11-27 21 views
6

Tôi muốn thực thi cấu trúc luôn hợp lệ về một hợp đồng nhất định, được thi hành bởi nhà xây dựng. Tuy nhiên, hợp đồng bị vi phạm bởi nhà điều hành default.Làm cách nào để thực thi hợp đồng trong một cấu trúc

Hãy xem xét những điều sau đây, ví dụ:

struct NonNullInteger 
{ 
    private readonly int _value; 

    public int Value 
    { 
     get { return _value; } 
    } 

    public NonNullInteger(int value) 
    { 
     if (value == 0) 
     { 
      throw new ArgumentOutOfRangeException("value"); 
     } 

     _value = value; 
    } 
} 

// Somewhere else: 
var i = new NonNullInteger(0); // Will throw, contract respected 
var j = default(NonNullInteger); // Will not throw, contract broken 

Là một workaround tôi đã thay đổi cấu trúc của tôi đến một lớp học để tôi có thể đảm bảo các nhà xây dựng luôn được gọi khi khởi tạo một thể hiện mới. Nhưng tôi tự hỏi, liệu có hoàn toàn không có cách nào để có được hành vi tương tự với cấu trúc?

Trả lời

3

Một cách tiếp cận là để sắp xếp mọi thứ để các giá trị mặc định đáp ứng các hợp đồng:

struct NonNullInteger 
{ 
    private readonly int _valueMinusOne; 

    public int Value 
    { 
     get { return _valueMinusOne + 1; } 
    } 

    public NonNullInteger(int value) 
    { 
     if (value == 0) 
     { 
      throw new ArgumentOutOfRangeException("value"); 
     } 

     _valueMinusOne = value - 1; 
    } 
} 
+0

này thực sự là một giải pháp tốt để đáp ứng thư của hợp đồng, mặc dù nó cũng sẽ cần kiểm tra int.MinValue. Tôi sẽ không khuyên bạn nên tạo cấu trúc này, nhưng, nếu bạn thực sự muốn làm điều đó, đây có lẽ là giải pháp sạch nhất. –

+0

+1 Bí quyết thông minh. Có thể khá khó khăn hơn cho các hợp đồng phức tạp hơn tuy nhiên. –

6

Tôi không thấy làm thế nào bạn có thể làm điều này, bởi vì, không giống như một lớp học, a struct always has a default parameterless constructor; cho cách cấu trúc của bạn được viết, giá trị 0 không thể bị ngăn cản:

Đường dẫn không thể chứa các hàm tạo không có tham số rõ ràng. Cấu trúc thành viên được tự động khởi tạo thành giá trị mặc định của chúng.

1

Lớp không thể thay đổi được ưu tiên hơn trong trường hợp này, vì trạng thái mặc định không hợp lệ. Đó là một chút tốn kém hơn như xa như sử dụng bộ nhớ, nhưng điều đó không quan trọng trừ khi bạn sử dụng một số lượng rất lớn trong số này. Mặc dù vậy, một ràng buộc 'số không khác' có thể được xử lý tốt hơn ở mức hợp đồng cho mỗi phương thức, thay vì đưa vào một lớp.

Nếu bạn thực sự muốn thực thi hợp đồng, hãy đặt ngoại lệ trong giá trị getter thay vì hàm tạo. Sau đó, hợp đồng là bạn sẽ ném một ngoại lệ nếu nó đã bao giờ có giá trị 0; lợi ích thực sự duy nhất ở đây là bạn không bao giờ âm thầm sử dụng một giá trị bằng không. Nhược điểm là bây giờ bạn có một so sánh mỗi khi bạn sử dụng giá trị.

1

Trong khi bạn không thể đạt được chính xác những gì bạn muốn, bạn có thể xác nhận trong getter:

public int Value 
{ 
    get 
    { 
     if (_value == 0) 
      throw new InvalidOperationException("Use of uninitialized struct"); 
     return _value; 
    } 
} 
Các vấn đề liên quan