Me encontré con este problema por un caso más simple de querer un método estático genérico que pudiera tomar cualquier cosa "anulable" (ya sea tipos de referencia o Nullables), lo que me llevó a esta pregunta sin una solución satisfactoria. Así que se me ocurrió mi propia solución, que era relativamente más fácil de resolver que la pregunta indicada por el OP al tener simplemente dos métodos sobrecargados, uno que toma un T
y tiene la restricción where T : class
y otro que toma unT?
tiene where T : struct
.
Luego me di cuenta de que esa solución también se puede aplicar a este problema para crear una solución que se pueda verificar en tiempo de compilación haciendo que el constructor sea privado (o protegido) y utilizando un método de fábrica estático:
//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);
}
}
Ahora podemos usarlo así:
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
Si desea un constructor sin parámetros, no obtendrá la delicadeza de la sobrecarga, pero aún puede hacer algo como esto:
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?>();
}
}
Y utilícelo así:
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
Hay algunas desventajas en esta solución, una es que puede preferir usar 'nuevo' para construir objetos. Otra es que usted no será capaz de utilizar Foo<T>
como un argumento de tipo genérico para una restricción tipo de algo como: where TFoo: new()
. Por último, está el código extra que necesita aquí, que aumentaría especialmente si necesita varios constructores sobrecargados.