146

So I have this class:

public class Foo<T> where T : ???
{
    private T item;

    public bool IsNull()
    {
        return item == null;
    }

}

Now I am looking for a type constraint that allows me to use everything as type parameter that can be null. That means all reference types, as well as all the Nullable (T?) types:

Foo<String> ... = ...
Foo<int?> ... = ...

should be possible.

Using class as the type constraint only allows me to use the reference types.

Additional Information: I am writing a pipes and filters application, and want to use a null reference as the last item that passes into the pipeline, so that every filter can shut down nicely, do cleanup, etc...

davidg
  • 5,868
  • 2
  • 33
  • 51
jkammerer
  • 1,577
  • 2
  • 10
  • 6
  • 1
    @Tim that doesn't allow for Nullables – Rik Nov 07 '13 at 08:36
  • This link may help you : http://social.msdn.microsoft.com/Forums/en-US/df6bd378-7ff9-4298-852d-a92deecc77e7/nullable-constraint – Réda Mattar Nov 07 '13 at 08:37
  • 2
    It's not possible to do this directly. Perhaps you can tell us more about your scenario? Or perhaps you could use `IFoo` as the working type and create instances through a factory method? That could be made to work. – Jon Nov 07 '13 at 08:50
  • I'm not sure why you would want or need to constrain something this way. If your only intent is to turn "if x == null" into if x.IsNull()" this seems pointless and unintuitive to the 99.99% of developers who are used to the former syntax. The compiler won't let you do "if (int)x == null" anyway, so you're already covered. – RJ Lohan Nov 07 '13 at 08:54
  • may be you can implement this method as pair of generic static methods:`IsItNull(Nullable i) where T : struct` and `IsItNull(T i) where T : class` – Guru Stron Nov 07 '13 at 08:56
  • 1
    This is pretty widely discussed on SO. http://stackoverflow.com/questions/209160/nullable-type-as-a-generic-parameter-possible?rq=1 and http://stackoverflow.com/questions/13794554/why-does-nullablet-not-match-as-a-reference-type-for-generic-constraints?lq=1 – Maxim Gershkovich Nov 07 '13 at 08:58
  • @RJLohan There might be many reasons for a non-nullable constraint to exist. But I can't think of one right now. But there are type constraints for non-nullable value type, non-nullable reference type, nullable or non-nullable reference type. As for a reason for nullable: I just need to be able to return `null` when return type is generic. And after all, for completeness/consistency. – mireazma Oct 12 '20 at 22:10
  • Although not a fit-all solution, in many cases `new()` could fill in the role of a nullable constraint. In fact I'd be thankful to learn some counterexamples, apart from the factory and singleton patterns. – mireazma Oct 12 '20 at 22:21

8 Answers8

24

I don't know how to implement equivalent to OR in generics. However I can propose to use default key word in order to create null for nullable types and 0 value for structures:

public class Foo<T>
{
    private T item;

    public bool IsNullOrDefault()
    {
        return Equals(item, default(T));
    }
}

You could also implement you version of Nullable:

class MyNullable<T> where T : struct
{
    public T Value { get; set; }

    public static implicit operator T(MyNullable<T> value)
    {
        return value != null ? value.Value : default(T);
    }

    public static implicit operator MyNullable<T>(T value)
    {
        return new MyNullable<T> { Value = value };
    }
}

class Foo<T> where T : class
{
    public T Item { get; set; }

    public bool IsNull()
    {
        return Item == null;
    }
}

Example:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(new Foo<MyNullable<int>>().IsNull()); // true
        Console.WriteLine(new Foo<MyNullable<int>> {Item = 3}.IsNull()); // false
        Console.WriteLine(new Foo<object>().IsNull()); // true
        Console.WriteLine(new Foo<object> {Item = new object()}.IsNull()); // false

        var foo5 = new Foo<MyNullable<int>>();
        int integer = foo5.Item;
        Console.WriteLine(integer); // 0

        var foo6 = new Foo<MyNullable<double>>();
        double real = foo6.Item;
        Console.WriteLine(real); // 0

        var foo7 = new Foo<MyNullable<double>>();
        foo7.Item = null;
        Console.WriteLine(foo7.Item); // 0
        Console.WriteLine(foo7.IsNull()); // true
        foo7.Item = 3.5;
        Console.WriteLine(foo7.Item); // 3.5
        Console.WriteLine(foo7.IsNull()); // false

        // var foo5 = new Foo<int>(); // Not compile
    }
}
Ryszard Dżegan
  • 24,366
  • 6
  • 38
  • 56
  • The original Nullable in the framework is a struct, not a class. I don't think it's a good idea to create a reference type wrapper that will mimic a value type. – Niall Connaughton Feb 09 '15 at 11:32
  • 1
    The first suggestion using **default** is perfect! Now my template with a generic type being returned can return a null for objects and the default value for built-in types. – Casey Anderson Aug 03 '16 at 18:50
  • 2
    @CaseyAnderson Unless the default value is meaningful, which in many cases it is. – Dai Jul 20 '21 at 17:05
24

If you are willing to make a runtime check in Foo's constructor rather than having a compile-time check, you can check if the type is not a reference or nullable type, and throw an exception if that's the case.

I realise that only having a runtime check may be unacceptable, but just in case:

public class Foo<T>
{
    private T item;

    public Foo()
    {
        var type = typeof(T);

        if (Nullable.GetUnderlyingType(type) != null)
            return;

        if (type.IsClass)
            return;

        throw new InvalidOperationException("Type is not nullable or reference type.");
    }

    public bool IsNull()
    {
        return item == null;
    }
}

Then the following code compiles, but the last one (foo3) throws an exception in the constructor:

var foo1 = new Foo<int?>();
Console.WriteLine(foo1.IsNull());

var foo2 = new Foo<string>();
Console.WriteLine(foo2.IsNull());

var foo3= new Foo<int>();  // THROWS
Console.WriteLine(foo3.IsNull());
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 38
    If you're going to do this, make sure you do the check in the *static* constructor, otherwise you'll be slowing down the construction of every instance of your generic class (unnecessarily) – Eamon Nerbonne Apr 05 '14 at 09:24
  • 2
    @EamonNerbonne You should not raise exceptions from static constructors: https://msdn.microsoft.com/en-us/library/bb386039.aspx – Matthew Watson Mar 16 '17 at 10:30
  • 8
    Guidelines aren't absolutes. If you want this check, you're going to have to trade-off the cost of a runtime check vs. the unhandiness of exceptions in a static constructor. Since you're really implementing a poor-mans static analyzer here, this exception should never be thrown except during development. Finally, even if you want to avoid static construction exceptions at all costs (unwise), then you should still do as much work as possible statically and as little as possible in the instance constructor - e.g. by setting a flag "isBorked" or whatever. – Eamon Nerbonne Mar 25 '17 at 13:08
  • Incidentally, I don't think you should try to do this at all. In most circumstances I'd prefer to just accept this as a C# limitation, rather than try and work with a leaky, failure-prone abstraction. E.g. a different solution might be to just require classes, or just require structs (and explicitly make em nullable) - or do both and have two versions. That's not a criticism of this solution; it's just that this problem cannot be solved well - unless, that is, you're willing to write a custom roslyn analyzer. – Eamon Nerbonne Mar 25 '17 at 13:16
  • 1
    You can get the best of both worlds - keep a `static bool isValidType` field that you set in the static constructor, then just check that flag in the instance constructor and throw if it's an invalid type so you're not doing all the checking work each time you construct an instance. I use this pattern often. – Mike Marynowski Sep 07 '18 at 03:53
  • @MikeMarynowski I think that's what Eamon said in his second comment? `e.g. by setting a flag "isBorked" or whatever.` - I think that's the same idea. – Matthew Watson Sep 07 '18 at 09:21
  • Oh oops, not sure how I missed that part. In that case I'm not sure why he's suggesting that's not a good solution in the next comment. It seems like a perfectly good solution to me and has come in really handy in many situations, but to each their own I guess. I think 99% of the time it is better to avoid static constructor exceptions at all costs, they are incredibly annoying to debug. – Mike Marynowski Sep 07 '18 at 13:14
  • @MikeMarynowski Yes, I agree that it's a good solution (and in fact, it's what I actually do myself). – Matthew Watson Sep 07 '18 at 13:56
  • 1
    Static constructor setting a flag that throws an exception in the normal constructor is a good idea. Unless it's a generic method, in which case there is no constructor. In that case, you can validate on every method call within a `#if DEBUG` section. Those exceptions normally should only be thrown during development. – Etienne Charland Sep 09 '20 at 19:12
22

I ran into this issue for a simpler case of wanting a generic static method that could take anything "nullable" (either reference types or Nullables), which brought me to this question with no satisfactory solution. So I came up with my own solution which was relatively easier to solve than the OP's stated question by simply having two overloaded methods, one that takes a T and has the constraint where T : class and another that takes a T? and has where T : struct .

I then realized, that solution can also be applied to this problem to create a solution that is checkable at compile time by making the constructor private (or protected) and using a static factory method:

    //this class is to avoid having to supply generic type arguments 
    //to the static factory call (see CA1000)
    public static class Foo
    {
        public static Foo<TFoo> Create<TFoo>(TFoo value)
            where TFoo : class
        {
            return Foo<TFoo>.Create(value);
        }

        public static Foo<TFoo?> Create<TFoo>(TFoo? value)
            where TFoo : struct
        {
            return Foo<TFoo?>.Create(value);
        }
    }

    public class Foo<T>
    {
        private T item;

        private Foo(T value)
        {
            item = value;
        }

        public bool IsNull()
        {
            return item == null;
        }

        internal static Foo<TFoo> Create<TFoo>(TFoo value)
            where TFoo : class
        {
            return new Foo<TFoo>(value);
        }

        internal static Foo<TFoo?> Create<TFoo>(TFoo? value)
            where TFoo : struct
        {
            return new Foo<TFoo?>(value);
        }
    }

Now we can use it like this:

        var foo1 = new Foo<int>(1); //does not compile
        var foo2 = Foo.Create(2); //does not compile
        var foo3 = Foo.Create(""); //compiles
        var foo4 = Foo.Create(new object()); //compiles
        var foo5 = Foo.Create((int?)5); //compiles

If you want a parameterless constructor, you won't get the nicety of overloading, but you can still do something like this:

    public static class Foo
    {
        public static Foo<TFoo> Create<TFoo>()
            where TFoo : class
        {
            return Foo<TFoo>.Create<TFoo>();
        }

        public static Foo<TFoo?> CreateNullable<TFoo>()
            where TFoo : struct
        {
            return Foo<TFoo?>.CreateNullable<TFoo>();
        }
    }

    public class Foo<T>
    {
        private T item;

        private Foo()
        {
        }

        public bool IsNull()
        {
            return item == null;
        }

        internal static Foo<TFoo> Create<TFoo>()
            where TFoo : class
        {
            return new Foo<TFoo>();
        }

        internal static Foo<TFoo?> CreateNullable<TFoo>()
            where TFoo : struct
        {
            return new Foo<TFoo?>();
        }
    }

And use it like this:

        var foo1 = new Foo<int>(); //does not compile
        var foo2 = Foo.Create<int>(); //does not compile
        var foo3 = Foo.Create<string>(); //compiles
        var foo4 = Foo.Create<object>(); //compiles
        var foo5 = Foo.CreateNullable<int>(); //compiles

There are few disadvantages to this solution, one is that you may prefer using 'new' to construct objects. Another is that you won't be able to use Foo<T> as a generic type argument for a type constraint of something like: where TFoo: new(). Finally is the bit of extra code you need here which would increase especially if you need multiple overloaded constructors.

Dave M
  • 2,863
  • 1
  • 22
  • 17
10

As mentioned, you cannot have a compile-time check for it. Generic constraints in .NET are severely lacking, and do not support most scenarios.

However I consider this to be a better solution for run-time checking. It can be optimized at JIT compilation time, since they're both constants.

public class SomeClass<T>
{
    public SomeClass()
    {
        // JIT-compile time check, so it doesn't even have to evaluate.
        if (default(T) != null)
            throw new InvalidOperationException("SomeClass<T> requires T to be a nullable type.");

        T variable;
        // This still won't compile
        // variable = null;
        // but because you know it's a nullable type, this works just fine
        variable = default(T);
    }
}
Aidiakapi
  • 6,034
  • 4
  • 33
  • 62
3

Such a type constraint is not possible. According to the documentation of type constraints there is not constraint that captures both the nullable and the reference types. Since constraints can only be combined in a conjunction, there is no way to create such a constraint by combination.

You can, however, for your needs fall back to an unconstraint type parameter, since you can always check for == null. If the type is a value type the check will just always evaluate to false. Then you'll possibly get the R# warning "Possible compare of value type with null", which is not critical, as long as the semantics is right for you.

An alternative could be to use

object.Equals(value, default(T))

instead of the null check, since default(T) where T : class is always null. This, however, means that you cannot distinguish weather a non-nullable value has never been set explicitly or was just set to its default value.

Sven Amann
  • 565
  • 2
  • 12
  • I think that the problem is how to check that value have never be set. Different than null seems to point that value have been initialized. – Ryszard Dżegan Nov 07 '13 at 08:58
  • That does not invalidate the approach, since value types are always set (at least implicitly to their respective default value). – Sven Amann Nov 07 '13 at 08:59
3

I use

public class Foo<T> where T: struct
{
    private T? item;
}
ela
  • 39
  • 1
2

If you only want to allow nullable value types and reference types, and disallow non-nullable value types, then I think you're out of luck as of C# 9.

I am writing a pipes and filters application, and want to use a null reference as the last item that passes into the pipeline, so that every filter can shut down nicely, do cleanup, etc...

In other words, you need to reserve a special value that indicates the end-of-stream.

Consider creating a wrapper type that provides this. It'd be similar to how Nullable<T> is implemented, and has the additional benefit of allowing a non-end-of-stream null value to be transmitted, should that be useful.

public readonly struct StreamValue<T>
{
    public bool IsEndOfStream { get; }
    public T Value { get; }
}
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
-2
    public class Foo<T>
    {
        private T item;

        public Foo(T item)
        {
            this.item = item;
        }

        public bool IsNull()
        {
            return object.Equals(item, null);
        }
    }

    var fooStruct = new Foo<int?>(3);
        var b = fooStruct.IsNull();

        var fooStruct1 = new Foo<int>(3);
        b = fooStruct1.IsNull();

        var fooStruct2 = new Foo<int?>(null);
        b = fooStruct2.IsNull();

        var fooStruct3 = new Foo<string>("qqq");
        b = fooStruct3.IsNull();

        var fooStruct4 = new Foo<string>(null);
        b = fooStruct4.IsNull();
SeeSharp
  • 1,730
  • 11
  • 31
  • This typing allows new Foo(42) and IsNull() will return false, which, whilst semantically correct, is not particularly meaningful. – RJ Lohan Nov 07 '13 at 09:05
  • 1
    42 is "The Answer to the Ultimate Question of Life, the Universe, and Everything". Simply put: IsNull for every int value will return false (even for 0 value). – Ryszard Dżegan Nov 07 '13 at 09:31