2013-02-15 31 views
14

Tôi đang tìm kiếm cái gì sử dụng mẫu như thế này:Constrain chung là một loại nullable

Foo<string> stringFoo = new Foo<string>("The answer is"); 
Foo<int> intFoo = new Foo<int>(42); 
// The Value of intFoo & stringFoo are strongly typed 
stringFoo.Nullify(); 
intFoo.Nullify(); 
if (stringFoo == null && intFoo == null) 
    MessageBox.Show("Both are null); 

Với lớp này Foo, tôi có thể tự động quấn T thành một nullable:

public class Foo1<T> 
    where T : struct 
{ 
    private T? _value; 

    public Foo(T? initValue) 
    { 
     _value = initValue; 
    } 

    public T? Value { get { return _value; } } 

    public void Nullify { _value = null; } 

} 

Điều này làm việc cho nguyên thủy, nhưng không phải với String hoặc các lớp khác.

hương vị Tiếp làm việc cho chuỗi, nhưng không phải nguyên thủy:

public class Foo2<T> 
{ 
    private T _value; 

    public Foo(T initValue) 
    { 
     _value = initValue; 
    } 

    public T Value { get { return _value; } } 

    public void Nullify { _value = default(T); } 

} 

tôi có thể sử dụng Nullable<int> cho foo2 và mã sẽ làm việc như thế này:

Foo2<int?> intFoo = new Foo<int?>(42); 

Nhưng điều này là dễ bị lỗi vì nó không thành công cho Foo2. Nếu tôi có thể hạn chế T là các loại hỗ trợ nullability thì điều đó sẽ ổn thôi.

Vì vậy, sau khi tất cả điều đó, có cách nào để hạn chế T là một loại nullable không?

Một số lưu ý bổ sung: .NET 4.0, VS2010. Và tôi đã tìm thấy một câu hỏi tương tự như vậy ở đây, nhưng không có câu trả lời thành công.

Trả lời

9

Bạn có thể có thể làm cho các nhà xây dựng của Foo<T> nội bộ, và yêu cầu các trường hợp mới chỉ có thể được tạo ra thông qua một lớp nhà máy:

public class Foo<T> 
{ 
    private T value; 
    internal Foo(T value) 
    { 
     this.value = value; 
    } 

    public void Nullify() 
    { 
     this.value = default(T); 
    } 

    public T Value { get { return this.value; } } 
} 

public class Foo 
{ 
    public static Foo<T> Create<T>(T value) where T : class 
    { 
     return new Foo<T>(value); 
    } 

    public static Foo<T?> Create<T>(T? value) where T : struct 
    { 
     return new Foo<T?>(value); 
    } 
} 
+0

Thú vị. Trong cuộc sống thực, Foo phức tạp hơn và được tạo tự động bởi một khung công tác. Tôi sẽ cần phải xem nếu có một cách để móc/tùy chỉnh cách thức các đối tượng được tạo ra để làm cho phương pháp này khả thi. – tcarvin

+0

Được chấp nhận, vì nó mang lại sự ngạc nhiên ít nhất cho khách hàng nội bộ của thư viện (thiết kế/biên dịch thời gian so với thời gian chạy). Nhưng bài kiểm tra được sử dụng bởi @ JonSkeet cũng tuyệt vời! – tcarvin

17

Không có hạn chế, bạn có thể áp dụng cho điều này, nhưng bạn thể kiểm tra cho nó ở thời gian thực hiện:

if (default(T) != null) 
{ 
    throw new SomeAppropriateException(typeof(T) + " is not a nullable type"); 
} 

Bạn có thể thậm chí đặt đó vào một constructor tĩnh, mà sẽ chắc chắn rằng nó chỉ thực hiện một lần cho mỗi loại được xây dựng - và bất kỳ ai đang cố gắng sử dụng Foo<int> ở bất cứ nơi nào sẽ có một thời gian khó bỏ qua TypeInitializerException. Đó không phải là điều rất thân thiện đối với một API công khai công khai, nhưng tôi cho rằng nó hợp lý đối với nội bộ.

EDIT: Có một khủng khiếp cách làm cho nó khó khăn hơn để tạo ra các trường hợp Foo<int> ... bạn có thể sử dụng (quy tắc sử dụng độ phân giải quá tải cùng với các thông số mặc định và một vài loại generic hạn chế) ghastly code in this blog post và đánh dấu quá tải nhằm vào một loại giá trị không nullable như lỗi thời với một lỗi. Bằng cách đó, Foo<int> vẫn sẽ là một loại hợp lệ, nhưng bạn sẽ khó ép để tạo ra một thể hiện của nó. Tôi sẽ không để khuyên bạn nên làm điều này mặc dù ...

+0

@NikolayKhil: Nhận xét bạn đang trả lời ở đây đã biến mất, nhưng mã trong câu trả lời này * sẽ * làm việc cho 'int? ', Vì kiểm tra tính rỗng" thực hiện điều đúng "cho đối số kiểu nullable. –

+0

Tôi có phải là các loại sai hoặc có thể sử dụng được không? Tôi có nghĩa là không có ràng buộc cho "loại nullable" nhưng có một hạn chế cho struct. Nhưng tôi đã làm thử nghiệm của riêng tôi và nếu, nếu bạn đưa ra một ràng buộc T: struct cho một số tham số chung, sau đó bạn không thể sử dụng một kiểu nullable làm đối số chung chung. Ví dụ: X sẽ không hoạt động với ràng buộc này. –

+0

Rõ ràng đây là vì int? là Nullable và Nullable là một kiểu tham chiếu, nhưng có lẽ .NET devteam có thể làm một số loại đường cú pháp để cho phép trường hợp sử dụng này –

0

tôi không thích nó nhiều như cú pháp của foo1, nhưng đây là Foo3:

public class Foo3<T> 
    where T : struct 
{ 

    private T _value; 
    private T _nullValue; 

    public Foo3(T initValue) 
     : this(initValue, default(T)) 
    { 
    } 

    public Foo3(T initValue, T nullValue) 
    { 
     _value = initValue; 
     _nullValue = nullValue; 
    } 

    public T Value { get { return _value; } } 

    public bool IsNull 
    { 
     get 
     { 
      return object.Equals(_value, _nullValue); 
     } 
    } 

    public void Nullify() { _value = _nullValue; } 

} 

Và sau đó sử dụng của tôi trở thành:

Foo3<string> stringFoo = new Foo<string>("The answer is"); 
Foo3<int> intFoo = new Foo<int>(42, int.MinValue); 
stringFoo.Nullify(); 
intFoo.Nullify(); 
if (stringFoo.IsNull && intFoo.IsNull) 
    MessageBox.Show("Both are null); 

Đây vẫn là lỗi dễ xảy ra vì việc nhận thuộc tính Giá trị của Foo3 (và Foo2) không đơn giản. Foo1 là tốt nhất bởi vì tự động bọc giá trị sẽ null hỗ trợ.

Tôi có thể chỉ cần ValueTypeFoo và ObjectFoo và xử lý hai câu.

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