654

I have a generic method with this (dummy) code (yes I'm aware IList has predicates, but my code is not using IList but some other collection, anyway this is irrelevant for the question...)

    static T FindThing<T>(IList collection, int id) where T : IThing, new()
    {
        foreach (T thing in collection)
        {
            if (thing.Id == id)
                return thing;
        }
        return null;  // ERROR: Cannot convert null to type parameter 'T' because it could be a value type. Consider using 'default(T)' instead.
    }

This gives me a build error

"Cannot convert null to type parameter 'T' because it could be a value type. Consider using 'default(T)' instead."

Can I avoid this error?

StefanFFM
  • 1,526
  • 1
  • 14
  • 25
edosoft
  • 17,121
  • 25
  • 77
  • 111
  • Would nullable reference types (in C# 8) be the better solution to this now? https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references Returning `null` regardless of whether `T` is `Object` or `int` or `char`. – Alexander May 23 '20 at 16:35
  • Too bad that `T?` in the signature doesn't work. @Alexander-ReinstateMonica I've read the article but couldn't see a way to return `null` regardless of reference or value type. – mireazma Oct 09 '20 at 08:03
  • 1
    @mireazma That's way too little information for me to be able to help. You should open a new question. – Alexander Oct 09 '20 at 14:03
  • The main problem I see is that you try to use `null` as indication that something is wrong there. I would propose to throw an exception instead. That's why exceptions exist and you can ommit wirting `if (... != null)` everywhere. – Markus Safar Feb 25 '22 at 13:48

13 Answers13

1140

Three options:

  • Return default (or default(T) for older versions of C#) which means you'll return null if T is a reference type (or a nullable value type), 0 for int, '\0' for char, etc. (Default values table (C# Reference))
  • If you're happy to restrict T to be a reference type with the where T : class constraint and then return null as normal
  • If you're happy to restrict T to be a non-nullable value type with the where T : struct constraint, then again you can return null as normal from a method with a return value of T? - note that this is not returning a null reference, but the null value of the nullable value type.
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    What if my return type is an enum not a class? I can't specify T : enum :( – Justin Aug 30 '11 at 02:01
  • 3
    In .NET an enum is a very thin (and rather leaky) wrapper around an integer type. The convention is to use zero for your "default" enum value. – Mike Chamberlain Mar 05 '12 at 03:53
  • 29
    I think the problem with this is that if you're using this generic method to say, convert a Database object from DbNull to Int and it returns default(T) where T is an int, it'll return 0. If this number is actually meaningful, then you'd be passing around bad data in cases where that field was null. Or a better example would be a DateTime. If the field was something like "DateClosed" and it was returned as null because and account is still open, it would actually default(DateTime) to 1/1/0000, implying that the account was closed before computers were invented. – Sinaesthetic Nov 20 '12 at 22:26
  • 24
    @Sinaesthetic: So you'd convert to `Nullable` or `Nullable` instead. If you use a non-nullable type and need to represent a null value, you're just asking for trouble. – Jon Skeet Nov 20 '12 at 22:28
  • 2
    I agree, I just wanted to bring it up. I think what I've been doing is more like MyMethod(); to assume it is a non nullable type and MyMethod(); to assume it is a nullable type. So in my scenarios, I could use a temp variable to catch a null and go from there. – Sinaesthetic Nov 20 '12 at 23:12
  • Since C# 7.1 (August 2017) you can simplify the syntax of option 1 a bit, into `return default;`, because the type is automatically inferred. – Jeppe Stig Nielsen Sep 22 '19 at 08:11
112
return default(T);
Ricardo Villamil
  • 5,031
  • 2
  • 30
  • 26
  • This link: http://msdn.microsoft.com/en-us/library/xwth0h0d(VS.80).aspx should explain why. – Harper Shelby Nov 19 '08 at 14:59
  • 1
    Damn it, I would've saved a lot of time had I known about this keyword - thanks Ricardo! – Ana Betts Nov 19 '08 at 15:06
  • 2
    I'm surprised this hasn't gotten more up votes as the 'default' keyword is a more comprehensive solution, allowing the use of non-reference types in conjunction with numeric types and structs. While the accepted answer solves the problem (and indeed is helpful), it better answers how to restrict return type to nullable/reference types. – Steve Jackson Sep 15 '14 at 18:30
47

You can just adjust your constraints:

where T : class

Then returning null is allowed.

TheSoftwareJedi
  • 34,421
  • 21
  • 109
  • 151
  • Thanks. I cannot choose 2 answers as the accepted solution, so I choose John Skeet's cause his answer has two solutions. – edosoft Nov 19 '08 at 15:16
  • @Migol it depends on your requirements. Maybe their project does require it be `IDisposable`. Yes, most of the time it doesn't have to be. `System.String` doesn't implement `IDisposable`, for example. The answerer should have clarified that, but that doesn't make the answer wrong. :) – ahwm Aug 30 '18 at 22:34
  • @Migol I have no clue why I had IDisposable in there. Removed. – TheSoftwareJedi Aug 31 '18 at 19:36
15

Add the class constraint as the first constraint to your generic type.

static T FindThing<T>(IList collection, int id) where T : class, IThing, new()
Min
  • 2,975
  • 1
  • 19
  • 24
  • Thanks. I cannot choose 2 answers as the accepted solution, so I choose John Skeet's cause his answer has two solutions. – edosoft Nov 19 '08 at 15:18
15

Below are the two option you can use

return default(T);

or

where T : class, IThing
 return null;
ANeves
  • 6,219
  • 3
  • 39
  • 63
Jaydeep Shil
  • 1,894
  • 22
  • 21
14
  1. If you have object then need to typecast

    return (T)(object)(employee);
    
  2. if you need to return null.

    return default(T);
    
bluish
  • 26,356
  • 27
  • 122
  • 180
7

Your other option would be to to add this to the end of your declaration:

    where T : class
    where T: IList

That way it will allow you to return null.

BFree
  • 102,548
  • 21
  • 159
  • 201
  • If both constraints are to the same type, you mention the type once and use a comma, like `where T : class, IList`. If you have constraints to different types, you repeat the token `where`, as in `where TFoo : class where TBar : IList`. – Jeppe Stig Nielsen Sep 22 '19 at 08:17
6

For completeness sake, it's good to know you could also do this:

return default;

It returns the same as return default(T);

LCIII
  • 3,102
  • 3
  • 26
  • 43
5

solution of TheSoftwareJedi works,

also you can archive it with using couple of value and nullable types:

static T? FindThing<T>(IList collection, int id) where T : struct, IThing
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return thing;
    }
    return null;
}
gdbdable
  • 4,445
  • 3
  • 30
  • 46
3

Here's a working example for Nullable Enum return values:

public static TEnum? ParseOptional<TEnum>(this string value) where TEnum : struct
{
    return value == null ? (TEnum?)null : (TEnum) Enum.Parse(typeof(TEnum), value);
}
Luke
  • 1
  • 2
  • 1
    Since C# 7.3 (May 2018), you can improve the constraint to `where TEnum : struct, Enum`. This ensures that a caller does not accidentally supply a value type that is not an enum (such as an `int` or a `DateTime`). – Jeppe Stig Nielsen Sep 22 '19 at 08:26
3

Because of IThing is interface is not possible to use null. So you have to use default(T) to determine default value for actual type T which is defined before function is called.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");
        
        IThing x = new List<Thing>().FindThing(1);
        
    }

}

public static class Ext {
    public static T FindThing<T>(this IList<T> collection, int id) where T : IThing, new()
    {
        foreach (T thing in collection)
        {
            if (thing.Id == id) return (T)thing;
        }
    
        //return null; //not work
        //return (T)null; //not work
        //return null as T; //not work
        return default(T); //work
    }
}

public interface IThing { int Id {get; set;} }
public class Thing : IThing { public int Id {get;set;}}
Mertuarez
  • 901
  • 7
  • 24
  • Did you try this code? Only `return default(T);` will work. – E. Shcherbo Mar 19 '21 at 16:39
  • @E.Shcherbo nope it work. Try it on fiddle. – Mertuarez Apr 13 '21 at 06:50
  • Okay, I didn't notice in your answer that your `IThing` is the class so your method knows that `T` is the class, but I'm sure that the author of the question means that `IThing` is an interface (what the `I` prefix is saying about) . – E. Shcherbo Apr 13 '21 at 16:57
  • @E.Shcherbo I have to apologize. You have right. Unfortunately question is not very verbose. Therefore for lazy people as me is not very feasible to achieve same conditions. :D – Mertuarez Apr 15 '21 at 07:07
  • no worries, despite the fact that your answer was about other concept (class vs interface) I still think that it was good. It showed the important point how the behaviour of a generic method differs when a type argument is known to be a class or an interface and also emphasised the fact that `where T : IThing` is also fine with value types. – E. Shcherbo Apr 15 '21 at 21:08
2

Take the recommendation of the error... and either user default(T) or new T.

You will have to add in a comparison in your code to ensure that it was a valid match if you go that route.

Otherwise, potentially consider an output parameter for "match found".

bluish
  • 26,356
  • 27
  • 122
  • 180
Mitchel Sellers
  • 62,228
  • 14
  • 110
  • 173
0

Another alternative to 2 answers presented above. If you change your return type to object, you can return null, while at the same time cast the non-null return.

static object FindThing<T>(IList collection, int id)
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return (T) thing;
    }
    return null;  // allowed now
}
Jeson Martajaya
  • 6,996
  • 7
  • 54
  • 56
  • Downside: this would require the caller of the method to cast the returned object (in non-null case), which implies boxing -> less performance. Am I right? – Csharpest Feb 19 '20 at 10:44