Tôi gặp phải vấn đề này vì một trường hợp đơn giản hơn là muốn một phương thức tĩnh chung chung có thể lấy bất kỳ thứ gì "nullable" (hoặc kiểu tham chiếu hoặc Nullables), điều này khiến tôi không có giải pháp thỏa đáng. Vì vậy, tôi đã đưa ra giải pháp của riêng mình tương đối dễ giải hơn so với câu hỏi đã nêu của OP bằng cách chỉ cần có hai phương thức nạp chồng, một phương thức nhận a T
và có ràng buộc where T : class
và một phương thức khác nhận a T?
và có where T : struct
.
Sau đó, tôi nhận ra rằng giải pháp đó cũng có thể được áp dụng cho vấn đề này để tạo ra một giải pháp có thể kiểm tra được tại thời điểm biên dịch bằng cách đặt hàm tạo là riêng tư (hoặc được bảo vệ) và sử dụng phương thức nhà máy tĩnh:
//this class is to avoid having to supply generic type arguments
//to the static factory call (see CA1000)
public static class Foo
{
public static Foo<TFoo> Create<TFoo>(TFoo value)
where TFoo : class
{
return Foo<TFoo>.Create(value);
}
public static Foo<TFoo?> Create<TFoo>(TFoo? value)
where TFoo : struct
{
return Foo<TFoo?>.Create(value);
}
}
public class Foo<T>
{
private T item;
private Foo(T value)
{
item = value;
}
public bool IsNull()
{
return item == null;
}
internal static Foo<TFoo> Create<TFoo>(TFoo value)
where TFoo : class
{
return new Foo<TFoo>(value);
}
internal static Foo<TFoo?> Create<TFoo>(TFoo? value)
where TFoo : struct
{
return new Foo<TFoo?>(value);
}
}
Bây giờ chúng ta có thể sử dụng nó như thế này:
var foo1 = new Foo<int>(1); //does not compile
var foo2 = Foo.Create(2); //does not compile
var foo3 = Foo.Create(""); //compiles
var foo4 = Foo.Create(new object()); //compiles
var foo5 = Foo.Create((int?)5); //compiles
Nếu bạn muốn một phương thức khởi tạo không tham số, bạn sẽ không gặp phải tình trạng quá tải, nhưng bạn vẫn có thể làm như sau:
public static class Foo
{
public static Foo<TFoo> Create<TFoo>()
where TFoo : class
{
return Foo<TFoo>.Create<TFoo>();
}
public static Foo<TFoo?> CreateNullable<TFoo>()
where TFoo : struct
{
return Foo<TFoo?>.CreateNullable<TFoo>();
}
}
public class Foo<T>
{
private T item;
private Foo()
{
}
public bool IsNull()
{
return item == null;
}
internal static Foo<TFoo> Create<TFoo>()
where TFoo : class
{
return new Foo<TFoo>();
}
internal static Foo<TFoo?> CreateNullable<TFoo>()
where TFoo : struct
{
return new Foo<TFoo?>();
}
}
Và sử dụng nó như thế này:
var foo1 = new Foo<int>(); //does not compile
var foo2 = Foo.Create<int>(); //does not compile
var foo3 = Foo.Create<string>(); //compiles
var foo4 = Foo.Create<object>(); //compiles
var foo5 = Foo.CreateNullable<int>(); //compiles
Có một vài nhược điểm đối với giải pháp này, một là bạn có thể thích sử dụng 'mới' để xây dựng các đối tượng. Một là bạn sẽ không thể sử dụng Foo<T>
như một đối số kiểu chung chung cho một loại hạn chế của một cái gì đó như: where TFoo: new()
. Cuối cùng là một chút mã bổ sung bạn cần ở đây sẽ tăng lên, đặc biệt nếu bạn cần nhiều hàm tạo quá tải.