Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Returning null for a nullable enum not allowed

I’ve written a simple extension method to help parse a string to a nullable enum type.

public static TEnum? ParseNullableEnum<TEnum>(this string? str)
    where TEnum : Enum
{
    if (str is null)
    {
        return null;
    }

    if (!Enum.TryParse(typeof(TEnum), str, ignoreCase: true, out var source))
    {
        throw new ArgumentOutOfRangeException(nameof(str), str, null);
    }

    return (TEnum)source;
}

As you can see, the return type is TEnum?, where TEnum is an Enum. And nullable is enabled, so null is a valid value. However, I’m getting an error on line return null;

CS0403: Cannot convert null to type parameter ‘TEnum’ because it could be a non-nullable value type. Consider using default(‘TEnum’) instead.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Is this a VS bug, or am I doing something wrong? I’m not converting to TEnum, the output type is TEnum?… right?

>Solution :

You want:

public static TEnum? ParseNullableEnum<TEnum>(this string? str)
    where TEnum : struct, Enum

The problem you have is that the Enum constraint can match any specific enum type, or the Enum class itself. (Enum is a weird one: it’s the base type of all enum types, but is itself a reference type. It basically represents a boxed enum type, i.e. you can write Enum foo = condition ? ConsoleColor.Black : ConsoleKey.A;, to pick on two random different enum types).

In other words, someone could call:

ParseNullableEnum<Enum>("thing")

Adding the struct constraint forces someone to actually pass a specific enum type, rather than Enum.

The other part of the puzzle is that if a generic type parameter T is not constrained to class or struct, then T? means that T is "defaultable" rather than "nullable". This means that:

  • If T is a reference type, T? allows null to be assigned
  • If T is a value type, T? does not mean Nullable<T>, it just means the same as T.

This is due to a difference in how T? is implemented for reference and value types by the compiler. For a value type, T? is shorthand for the type Nullable<T>, but for reference types T? compiles to the same thing as T but has an effect on compiler warnings only. It’s not possible for the compiler to emit a method where the return type is T when T is a reference type, but Nullable<T> when T is a value type.

So, by not constraining T to a reference or value type, your return type of TEnum? did not mean Nullable<TEnum>, it just meant TEnum with different nullable warnings. By constraining TEnum to a value type, the compiler is allowed to emit a method which actually returns Nullable<TEnum>, and so is allowed to have the value null.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading