So I have this class:
public class Foo where T : ???
{
private T item;
public bool IsNull()
{
return item == null;
}
}
I ran into this issue for a simpler case of wanting a generic static method that could take anything "nullable" (either reference types or Nullables), which brought me to this question with no satisfactory solution. So I came up with my own solution which was relatively easier to solve than the OP's stated question by simply having two overloaded methods, one that takes a T
and has the constraint where T : class
and another that takes a T?
and has where T : struct
.
I then realized, that solution can also be applied to this problem to create a solution that is checkable at compile time by making the constructor private (or protected) and using a static factory method:
//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 Create(TFoo value)
where TFoo : class
{
return Foo.Create(value);
}
public static Foo Create(TFoo? value)
where TFoo : struct
{
return Foo.Create(value);
}
}
public class Foo
{
private T item;
private Foo(T value)
{
item = value;
}
public bool IsNull()
{
return item == null;
}
internal static Foo Create(TFoo value)
where TFoo : class
{
return new Foo(value);
}
internal static Foo Create(TFoo? value)
where TFoo : struct
{
return new Foo(value);
}
}
Now we can use it like this:
var foo1 = new Foo(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
If you want a parameterless constructor, you won't get the nicety of overloading, but you can still do something like this:
public static class Foo
{
public static Foo Create()
where TFoo : class
{
return Foo.Create();
}
public static Foo CreateNullable()
where TFoo : struct
{
return Foo.CreateNullable();
}
}
public class Foo
{
private T item;
private Foo()
{
}
public bool IsNull()
{
return item == null;
}
internal static Foo Create()
where TFoo : class
{
return new Foo();
}
internal static Foo CreateNullable()
where TFoo : struct
{
return new Foo();
}
}
And use it like this:
var foo1 = new Foo(); //does not compile
var foo2 = Foo.Create(); //does not compile
var foo3 = Foo.Create(); //compiles
var foo4 = Foo.Create
There are few disadvantages to this solution, one is that you may prefer using 'new' to construct objects. Another is that you won't be able to use Foo
as a generic type argument for a type constraint of something like: where TFoo: new()
. Finally is the bit of extra code you need here which would increase especially if you need multiple overloaded constructors.