Ich bin auf dieses Problem gestoßen, weil es einfacher war, eine generische statische Methode zu wollen, die alles "Nullable" (entweder Referenztypen oder Nullables) annehmen kann, was mich zu dieser Frage ohne zufriedenstellende Lösung brachte. Also habe ich meine eigene Lösung gefunden, die relativ einfacher zu lösen war als die vom OP angegebene Frage, indem ich einfach zwei überladene Methoden hatte, eine, die a T
und die Einschränkung hat, where T : class
und eine, die a T?
und hat where T : struct
.
Dann wurde mir klar, dass diese Lösung auch auf dieses Problem angewendet werden kann, um eine Lösung zu erstellen, die beim Kompilieren überprüft werden kann, indem der Konstruktor privat (oder geschützt) gemacht und eine statische Factory-Methode verwendet wird:
//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);
}
}
Jetzt können wir es so verwenden:
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
Wenn Sie einen parameterlosen Konstruktor wollen, werden Sie nicht die Überladung bekommen, aber Sie können trotzdem so etwas tun:
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?>();
}
}
Und benutze es so:
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
Diese Lösung hat nur wenige Nachteile. Zum einen bevorzugen Sie möglicherweise die Verwendung von 'new' zum Erstellen von Objekten. Ein weiterer Grund ist, dass Sie nicht Foo<T>
als generisches Typargument für eine Typeinschränkung verwenden können, die Folgendes umfasst : where TFoo: new()
. Schließlich ist hier der zusätzliche Code, den Sie benötigen, insbesondere wenn Sie mehrere überladene Konstruktoren benötigen.