Search code examples
c#genericsnullnullable

Restricting a generic to things that can be null


I'd like to restrict a generic I'm coding to anything that can be null. That's basically any class + System.Nullable (e.g. int? and such).

For the class part, it's rather easy:

public class MyGeneric<T> where T : class {}

But then, this doesn't allow me to do this:

var myGeneric = new MyGeneric<int?>();

or this:

var myGeneric = new MyGeneric<Nullable<int>>();

The compiler complains with:
error CS0452: The type 'int?' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Test.MyGeneric'

So I tried addind System.Nullable as accepted types for T:

public class MyGeneric<T> where T : class, System.Nullable {}

But it won't do. The compiler returns the following error:
error CS0717: 'System.Nullable': static classes cannot be used as constraints

I then tried

public class MyGeneric<T> where T : class, INullable {}

It does compile, but then when I do:

var myGeneric = new MyGeneric<string>();

The compiler returns this error:
error CS0311: The type 'string' cannot be used as type parameter 'T' in the generic type or method 'Test.MyGeneric'. There is no implicit reference conversion from 'string' to 'System.Data.SqlTypes.INullable'.

So, the question is: Is it even possible to restrict a generic to anything that can be null, and of so, how?

For reference, I'm using VS2010 / C# 4.0

edit
I was asked what I want to do with it. Here's an example:

namespace Test
{
    public class MyGeneric<T> where T : class
    {
        private IEnumerable<T> Vals { get; set; }

        public MyGeneric(params T[] vals)
        {
            Vals = (IEnumerable<T>)vals;
        }

        public void Print()
        {
            foreach (var v in Vals.Where(v => v != default(T)))
            {
                Trace.Write(v.ToString());
            }
            Trace.WriteLine(string.Empty);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyGeneric<string> foo = 
                new MyGeneric<string>("a", "b", "c", null, null, "g");
            foo.Print();
        }
    }
}

This program prints abcg in the debug console.


Solution

  • No, there is no way to do this at compile-time.

    Personally, I'd just let T be anything, then I'd check its validity in a static constructor:

    public class MyGeneric<T>
    {
        static MyGeneric()
        {
            var def = default(T); 
            if (def is ValueType && Nullable.GetUnderlyingType(typeof(T)) == null)
            {
                throw new InvalidOperationException(
                    string.Format("Cannot instantiate with non-nullable type: {0}",
                        typeof(T)));
            }
        }
    }