I'm looking for sample usage something like this:
Foo<string> stringFoo = new Foo<string>("The answer is");
Foo<int> intFoo = new Foo<int>(42);
// The Value of intFoo & stringFoo are strongly typed
stringFoo.Nullify();
intFoo.Nullify();
if (stringFoo == null && intFoo == null)
MessageBox.Show("Both are null);
Given this class Foo, I can auto-wrap T into a nullable:
public class Foo1<T>
where T : struct
{
private T? _value;
public Foo(T? initValue)
{
_value = initValue;
}
public T? Value { get { return _value; } }
public void Nullify { _value = null; }
}
This works for primitives, but not with String or other classes.
Next flavor works for strings, but not primitives:
public class Foo2<T>
{
private T _value;
public Foo(T initValue)
{
_value = initValue;
}
public T Value { get { return _value; } }
public void Nullify { _value = default(T); }
}
I could use Nullable<int>
for Foo2 and the code would work like this:
Foo2<int?> intFoo = new Foo<int?>(42);
But this is error prone because it fails for Foo2. If I could constrain T to be types that support nullability then that would be fine.
So after all of that, is there any way to constrain T to be a nullable type?
Some additional notes: .NET 4.0, VS2010. And I did find one similar question to this on here, but without a succesful answer.
In c#, constraints are used to restrict generics to accept only the particular type of placeholders. By using where keyword, we can apply constraints on generics. In c#, you can apply multiple constraints on generic classes or methods based on your requirements.
A type constraint on a generic type parameter indicates a requirement that a type must fulfill in order to be accepted as a type argument for that type parameter. (For example, it might have to be a given class type or a subtype of that class type, or it might have to implement a given interface.)
An unbound generic type is not itself a type, and it cannot be used as the type of a variable, argument, or return value, or as a base type. The only construct in which an unbound generic type can be referenced is the typeof expression (§7.6. 11).
A generic type is declared by specifying a type parameter in an angle brackets after a type name, e.g. TypeName<T> where T is a type parameter.
There's no constraint you can apply for this, but you can test for it at execution time:
if (default(T) != null)
{
throw new SomeAppropriateException(typeof(T) + " is not a nullable type");
}
You could even put that into a static constructor, which would make sure it only executed once per constructed type - and anyone trying to use Foo<int>
anywhere would have a hard time ignoring the TypeInitializerException
. That's not terribly friendly for a public API, but I think it's reasonable for an internal one.
EDIT: There is one horrible way of making it harder to create instances of Foo<int>
... you could use the ghastly code in this blog post (using overload resolution rules along with default parameters and a couple of constrained generic types) and mark the overload which aims at a non-nullable value type as obsolete with an error. That way, Foo<int>
would still be a valid type, but you'd be hard-pressed to create an instance of it. I'm not going to recommend that you do this though...
You might be able to make the constructor of Foo<T>
internal, and require that new instances can only be created through a factory class:
public class Foo<T>
{
private T value;
internal Foo(T value)
{
this.value = value;
}
public void Nullify()
{
this.value = default(T);
}
public T Value { get { return this.value; } }
}
public class Foo
{
public static Foo<T> Create<T>(T value) where T : class
{
return new Foo<T>(value);
}
public static Foo<T?> Create<T>(T? value) where T : struct
{
return new Foo<T?>(value);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With