简体   繁体   中英

C# generic class using reference types and nullable value types

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:

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM