Version Used:
C# 7.3 (on Framework 4.7.2 and Core 2.2)
Steps to Reproduce:
public void MethodWithEnum<T>(T? arg) where T : Enum { }
Expected Behavior:
Method which works only with Nullable enums
Actual Behavior:
CS8627 - A nullable type parameter must be known to be a value type or non-nullable reference type. Consider adding a 'class', 'struct', or type constraint.
CS8652 - The feature 'nullable reference types' is currently in Preview and unsupported. To use Preview features, use the 'preview' language version.
Workaround:
public static void MethodWithEnum<T>(T? arg) where T : struct, Enum { }
Nullablewhere T : struct but can be used with enum.
In C# 7.3 it is possible to use where T : Enum:
enum MyEnum
{
Value
}
public static void Method<T>(T arg) where T : Enum { }
public static void Main()
{
MyEnum? value = null;
Method(value.Value);
}
Therefore, the following code should work correctly: public static void MethodWithEnum<T>(T? arg) where T : Enum { }
In C# 7.3 it is possible to use
where T : Enum
Maybe there is a typo in your example (MyEnum vs. Enum), but I didn't quite understand what you mean.
The following code is an error:
C#
enum Enum
{
Value
}
class C
{
public static void Method<T>(T arg) where T : Enum { } // error CS0701: 'Enum' is not a valid constraint. A type used as a constraint must be an interface, a non-sealed class or a type parameter.
public static void Main()
{
Enum? value = null;
Method(value.Value);
}
}
@jcouv here's the full example I believe, there're no typos but using was omitted which could lead to confusion.
using System;
class Program {
enum MyEnum
{
Value
}
public static void Method<T>(T arg) where T : Enum { }
// won't compile with `T? arg`
public static void Main()
{
MyEnum? value = null;
Method(value.Value);
}
}
Thanks for the clarification!
Therefore, the following code should work correctly:
public static void MethodWithEnum<T>(T? arg) where T : Enum { }
It can't. The problem is that the type Enum, which is a reference type, satisfies the Enum constraint. This is why you have to use where T : struct, Enum, as you discovered. I don't think that's a workaround, it's actually the right solution.
Went back and checked notes and the PR regarding System.Enum type constraint (https://github.com/dotnet/roslyn/pull/24199). That constraint does not imply a struct or class constraint. So the current behavior is by design.
Note that an alternative to a type constraint was considered (enum), which would did imply struct, but that proposal was ultimately rejected.
I'm not sure, can we have Enum that is not a struct, @jcouv ? Could you give an example, please?
There is only one that I know of: System.Enum ;-) (sharplab).
I'm not saying the difference is big, only that there is a difference. Also, I dug up the LDM notes that explicitly made this decision: https://github.com/dotnet/csharplang/blob/98043cdc889303d956d540d7ab3bc4f5044a9d3b/meetings/2018/LDM-2018-01-24.md#enum
Most helpful comment
@jcouv here's the full example I believe, there're no typos but
usingwas omitted which could lead to confusion.