Version Used:
C# 7.3 Preview in VS 15.7 Preview 3
Steps to Reproduce:
Trying to use this new feature: System.Enum, System.Delegate and unmanaged constraints.
Consider this code for extension method:
public static T ToEnum<T>(this string enumString, T defaultOnFailure = default(T), bool ignoreCase = true) where T : Enum =>
Enum.TryParse(enumString, ignoreCase, out T result) ? result : defaultOnFailure;
Expected Behavior:
It should compile, because Enum is in effect a subset of ValueType.
Actual Behavior:
It does not compile: "CS0453 The type 'T' must be a non-nullable value type in order to use it as parameter 'TEnum' in the generic type or method 'Enum.TryParse
As far as I can tell from this description, the Enum constraint does not mean the type is a value type, because it can also be Enum itself, which is a reference type.
For example, consider this code, which compiles fine:
```c#
class Program
{
static void Main()
{
Console.WriteLine(IsValueType
Console.WriteLine(IsValueType
}
static bool IsValueType<T>() where T : Enum => typeof(T).IsValueType;
}
To get the behavior you want, you need to specify both `struct` and `Enum` constraints:
```c#
public static T ToEnum<T>(this string enumString, T defaultOnFailure = default(T), bool ignoreCase = true) where T : struct, Enum =>
Enum.TryParse(enumString, ignoreCase, out T result) ? result : defaultOnFailure;
CC @OmarTawfik
Thank you! That seems like an obvious fix. Although to me it is still rather strange that the Enum constraint actually includes the base class Enum which is not a value type, instead of including just the actual enums, which is what it would be used in 99% cases.
@fitdev, strange indeed!
Iām also curious as to why they did not pick āenumā and ādelegateā (keywords) as constraints. Must have missed that discussion.
Iām also curious as to why they did not pick āenumā and ādelegateā (keywords) as constraints. Must have missed that discussion.
Adding "enum" and "delegate" would be adding a new feature (and can still be done in teh future). What happened here was simply that the existing restrictions on using System.Enum or System.Delegate were removed and they simply picked up the same behavior as all other named types that you can use as constraints.
Thank you! That seems like an obvious fix. Although to me it is still rather strange that the Enum constraint actually includes the base class Enum
It's not the "enum constraint" really. It's just a normal constraint on the .net type "System.Enum". In other words, it's not special to the language/compiler. When you add this constraint it's like adding a constraint of System.Whatever. It's always meant "that class or subtypes", it's never meant "only subtypes".
If you'd like an enum constraint that truly means "subtypes of System.Enum" i would recommend opening a bug over on csharplang asking for that :)
@CyrusNajmabadi understood, thanks for your reply!
As others already commented, the features just removed the language restriction. The compiler always supported T : Enum being imported from other binaries, but just didn't allow it in source.
@rcollina several people suggested using T : enum and T : delegate. They also suggested being able to specify the underlying type of enum T : enum(int) or the delegate signature T : delegate(???), but that would be a language change that requires many other details to be worked out.
@fitdev from comments, I understand the issue is now solved. Please re-open if you see otherwise.
Just installed 15.7.0 for just that single feature - and was just pissed within minutes. I'm sorry that I never before thought about System.Enum being some kind of magic value+ref kind both at the same time.
My suggestion would be to make where T: enum a short form of where T: struct, System.Enum.
@springy76 : https://github.com/dotnet/roslyn/issues/26066#issuecomment-380188341