为什么泛型类型受';枚举';不具备a'资格;结构';在C#7.3中?

为什么泛型类型受';枚举';不具备a'资格;结构';在C#7.3中?,c#,.net,generics,enums,c#-7.3,C#,.net,Generics,Enums,C# 7.3,如果我有一个带有如下约束的struct通用接口: public interface IStruct<T> where T : struct { } C#7.3增加了一个。以下代码以前是非法的,现在可以编译: public class MCVE<T> : IStruct<T> where T : struct, Enum { } public类MCVE:IStruct其中T:struct,Enum{} 然而,令我惊讶的是,以下内容未能编译: public

如果我有一个带有如下约束的
struct
通用接口:

public interface IStruct<T> where T : struct { }
C#7.3增加了一个。以下代码以前是非法的,现在可以编译:

public class MCVE<T> : IStruct<T> where T : struct, Enum { }
public类MCVE:IStruct其中T:struct,Enum{}
然而,令我惊讶的是,以下内容未能编译:

public class MCVE<T> : IStruct<T> where T : Enum { }
public类MCVE:IStruct其中T:Enum{}
…带着错误

CS0453类型“T”必须是不可为null的值类型才能使用 将其作为泛型类型或方法“IStruct”中的参数“T”

为什么会这样?我希望受
Enum
约束的泛型类型可用作类型参数,其中类型受
struct
约束,但情况似乎并非如此-我必须将
Enum
约束更改为
struct,Enum
。我的期望是错误的吗?

是奇怪的(可以说),但却是预期的行为

系统.Enum
本身可以作为
T
的类型提供。作为一个类,
System.Enum
当然不是一个
struct

public class MCVE<T> where T : Enum { }
public class MCVE2 : MCVE<Enum> { }
公共类MCVE,其中T:Enum{}
公共类MCVE2:MCVE{}
:

这是CLR本身的一种奇怪行为
System.Enum
是一个类,但是 从
System.Enum
派生的每个类型都是一个
struct
。这是一个限制 在
System.Enum
上,它本身并不意味着
struct
,因为您可以通过
System.Enum
作为泛型类型参数

这很奇怪,但简单地取消强加的限制更容易 而不是在编译器上争论“enum”的不同语法 可能具有不同行为的约束


解决方案是当您希望将具体类型约束为任何特定枚举时,将约束为
struct,Enum
作为您的标准实践。此外,如果您希望接受类
System.Enum
作为泛型类型,只有这样您才能约束到
Enum

,这并不奇怪,因为继承时必须重复约束。虽然您的答案中关于新的
Enum
约束的花边新闻很有趣,但这肯定是以前在C#中省略它的原因之一。CLR不区分System.Enum和具体的Enum类型作为实际类型参数(Ecma-355,第二章10.1.7)。这很重要,因为System.Enum是引用类型而不是值类型,所以必须对值进行装箱。您可能对实际使用System.Enum不感兴趣,所以通过添加struct containt来关闭它。这是一个完全无用的功能。您可以将类型约束为
Enum
,但不能对其执行任何操作!不能将其强制转换为“int”,不能将标志与
|
组合。这些都给出了错误:
TEnum val;var i=(int)val//错误:“无法将类型“TEnum”转换为“int”
TEnum a、b;var c=a | b//错误:“运算符“|”不能应用于类型“TEnum”和“TEnum”。
无用。我仍然想知道为什么不使用
enum
关键字来避免这种情况。甚至不能像枚举一样使用它。无法强制转换为(int),也无法将值与“|”组合。使用TEnum泛型值的唯一方法是使用Convert.ToInt32和整数,然后使用
(TEnum)(object)tenumValue
(TEnum)Enum.ToObject(enumType,tenumValue)
,ToObject是一个糟糕的名称(应该是FromValue)因为有文档记录了从纯整数值创建枚举对象。有趣的是,即使不使用枚举类型约束特性,这种变通方法也可以工作,所以它真的是完全无用的!
public class MCVE<T> where T : Enum { }
public class MCVE2 : MCVE<Enum> { }