Je suis tombé sur ce problème pour un cas plus simple de vouloir une méthode statique générique qui pourrait prendre n'importe quoi "nullable" (soit des types de référence ou Nullables), ce qui m'a amené à cette question sans solution satisfaisante. J'ai donc proposé ma propre solution qui était relativement plus facile à résoudre que la question posée par l'OP en ayant simplement deux méthodes surchargées, une qui prend a T
et a la contrainte where T : class
et une autre qui prend a T?
et a where T : struct
.
J'ai alors réalisé que cette solution pouvait également être appliquée à ce problème pour créer une solution vérifiable au moment de la compilation en rendant le constructeur privé (ou protégé) et en utilisant une méthode de fabrique statique:
//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);
}
}
Maintenant, nous pouvons l'utiliser comme ceci:
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 vous voulez un constructeur sans paramètre, vous n'obtiendrez pas la subtilité de la surcharge, mais vous pouvez toujours faire quelque chose comme ceci:
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?>();
}
}
Et utilisez-le comme ceci:
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
Il y a peu d'inconvénients à cette solution, l'un est que vous préférerez peut-être utiliser «nouveau» pour construire des objets. Une autre est que vous ne serez pas en mesure d'utiliser Foo<T>
comme argument de type générique pour une contrainte de type de quelque chose comme: where TFoo: new()
. Enfin, il y a le peu de code supplémentaire dont vous avez besoin ici, ce qui augmenterait surtout si vous avez besoin de plusieurs constructeurs surchargés.