3

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?

jjoelson
  • 5,771
  • 5
  • 31
  • 51
  • Note that `T x = null;` will only compile if `x` is nullable, but `T x = default(T)` and `x == null` will *always* compile (it just always returns false for non-nullable types). – Gabe Sep 20 '11 at 18:44
  • In your code, `typeof(Nullable)` refers to another type than you think, namely a static class which is non-generic. Instead one can say `type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)`. An alternative in this specific case is `Nullable.GetUnderlyingType(type) != null`. Also, it looks like you can use `typeof(T)` instead of what you call `type`. – Jeppe Stig Nielsen Jan 19 '14 at 14:15

2 Answers2

5

How about:

public class ClassWithNull<T>
{
    public T varName = default(T);
}

(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:

public static ClassWithNull<T> CreateClassWithNullForClass<T> where T : class
{
    return new ClassWithReferenceType<T>();
}

public static ClassWithNull<T> CreateClassWithNullForNullable<T> where T : struct
{
    return new ClassWithNullableValueType<T>();
}

The field in ClassWithNullableValueType would be Nullable<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.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I don't think he's interested in default(T) as it can be not null in some cases. His intention is rather to allow only to assign null ("must either be a reference type or a nullable value type"). – Wiktor Zychla Sep 20 '11 at 18:42
  • @Wiktor: `default(T)` is always the null value for any `T` which *has* a null value. But there's no constraint which will allow this to be expressed. See the rest of my answer for further options though... – Jon Skeet Sep 20 '11 at 18:45
  • Your edited answer contains just the solution I thought about. – Wiktor Zychla Sep 20 '11 at 18:48
  • Yeah, the first solution is not sufficient. However the second one is brilliant. I have no idea why having *two* factory methods never occurred to me. I'll accept this answer in three minutes (why does SO prevent the asker from accepting an answer within 10 mins?) – jjoelson Sep 20 '11 at 18:49
  • @WiktorZychla: Goodo. I think I'd probably still stick with the first approach if possible though :) – Jon Skeet Sep 20 '11 at 18:50
  • @jjoelson: It's so that others get a chance to add a good answer too. The first "well, it works" answer isn't necessarily the best one - sometimes someone will post a *better* answer a few minutes later. Maybe someone will do that in this case too :) – Jon Skeet Sep 20 '11 at 18:51
0

You should be able to do this instead:

public class ClassWithNull<T>
{
    private object varName_priv = null;

    public T varName {
        get { return (T)varName_priv; }
        set { varName_priv = value; }
    }
}

This works because every non-pointer type in C# is convertible to object, including value types.

Nikos C.
  • 50,738
  • 9
  • 71
  • 96