Я столкнулся с этой проблемой из-за более простого случая, когда мне нужен общий статический метод, который мог бы принимать все, что допускает значение NULL (ссылочные типы или Nullables), что привело меня к этому вопросу без удовлетворительного решения. Итак, я придумал свое собственное решение, которое было относительно проще решить, чем заданный в OP вопрос, просто имея два перегруженных метода, один из которых принимает a T
и имеет ограничение, where T : class
а другой принимает T?
и имеет where T : struct
.
Затем я понял, что это решение также может быть применено к этой проблеме для создания решения, которое можно проверить во время компиляции, сделав конструктор закрытым (или защищенным) и используя статический фабричный метод:
//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);
}
}
Теперь мы можем использовать это так:
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
Если вам нужен конструктор без параметров, вы не получите тонкости перегрузки, но вы все равно можете сделать что-то вроде этого:
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?>();
}
}
И используйте это так:
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
У этого решения есть несколько недостатков, один из них заключается в том, что вы можете предпочесть использовать «новый» для создания объектов. Другим является то , что вы не будете в состоянии использовать в Foo<T>
качестве общего аргумента типа для типа ограничения что - то вроде: where TFoo: new()
. Наконец, вам нужен дополнительный код, который может увеличиться, особенно если вам нужно несколько перегруженных конструкторов.