Roslyn: Nulalble enum constraint

Created on 1 May 2019  路  7Comments  路  Source: dotnet/roslyn

Version Used:
C# 7.3 (on Framework 4.7.2 and Core 2.2)

Steps to Reproduce:

  1. Create a project, set C# version to 7.3
  2. Declare method
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 { }

Nullable has constraint where 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 { }

Area-Compilers New Language Feature - Nullable Reference Types Resolution-By Design

Most helpful comment

@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);
    }
}

All 7 comments

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

Was this page helpful?
0 / 5 - 0 ratings