I have an interesting problem. I'd like to create a generic class that can deal with both Reference types as well as Nullable<T>
types. Basically I want something like:
public class ClassWithNull<T>
{
public T varName = null;
}
Now this, of course, does not compile because not all types can be assigned null, namely non-nullable value types. But the problem is Nullable<T>
is a value type, so simply adding where T : class
doesn't help me. My generics-foo is not too strong, but I haven't been able to find any way to say that T
must either be a reference type or a nullable value type.
The idea I have to solve this is to make ClassWithNull<T>
an abstract class. I could then add two sub-classes, one to deal with reference types and one to deal with nullable value types. Then, a static factory method in the base class could use reflection to determine which sub-class ought to be constructed. Something like:
public static ClassWithNull<T> CreateClassWithNull<T>()
{
StackTrace st = new StackTrace();
Type type = st.GetFrame(1).GetMethod().GetGenericArguments()[0];
if (!type.IsValueType)
{
return new ClassWithReferenceType<T>();
}
else if (type == typeof(Nullable))
{
return new ClassWithNullableValueType<T>();
}
else
{
throw new Exception("Must provide nullable type.");
}
}
The problem here is that generics are resolved statically. If ClassWithReferenceType<U>
expects U
to be a reference type, then calling new ClassWithReferenceType<T>()
in the factory method is a compilation error since T
is not required to be a reference type. The compiler does not know about the run time check.
Any ideas about how to implement such a thing?
How about:
(Actually, you don't even need the assignment - you can just leave it to be the default value on construction. But you might want
default(T)
for local variables.)That won't stop you from using it incorrectly with a non-nullable value type - but is that enough?
If that doesn't help you, I would suggest writing two static methods, like this:
The field in
ClassWithNullableValueType
would beNullable<T>
-T
would be the underlying type.Now if you want overloads of the same method, that gets a little harder, particularly if you don't want to pass any parameters. It's possible, but really, really horrible.
You should be able to do this instead:
This works because every non-pointer type in C# is convertible to
object
, including value types.