Limitaciones de tipo Enum en C #

Posible duplicado:
¿Alguien sabe una buena solución para la falta de una restricción genérica enum?

¿Cuál es la razón detrás de C # que no permite restricciones de tipo en Enum ? Estoy seguro de que hay un método detrás de la locura, pero me gustaría entender por qué no es posible.

A continuación se muestra lo que me gustaría poder hacer (en teoría).

 public static T GetEnum(this string description) where T : Enum { ... } 

Esta es una característica ocasionalmente solicitada.

Como me gusta señalar, TODAS las funciones no se implementan hasta que alguien diseña, especifica, implementa, prueba, documenta y envía la característica. Hasta ahora, nadie ha hecho eso para este. No hay una razón particularmente inusual por la que no; tenemos muchas otras cosas que hacer, presupuestos limitados, y este nunca ha pasado del “¿no sería lindo?” discusión en el equipo de diseño de idiomas.

El CLR no lo admite, por lo tanto, para que funcione, tendremos que hacer un trabajo en tiempo de ejecución además del trabajo de lenguaje. (ver comentarios de respuesta)

Puedo ver que hay algunos casos de uso decente, pero ninguno de ellos es tan convincente que hagamos este trabajo en lugar de uno de los cientos de otras características que se solicitan con mucha más frecuencia, o que tienen un scope más convincente y de mayor scope. casos de uso. (Si vamos a ensuciar con este código, yo personalmente daré prioridad a las restricciones de delegates muy por encima de las restricciones enum).

En realidad, es posible, con un truco feo. Sin embargo, no puede usarse para métodos de extensión.

 public abstract class Enums where Temp : class { public static TEnum Parse(string name) where TEnum : struct, Temp { return (TEnum)Enum.Parse(typeof(TEnum), name); } } public abstract class Enums : Enums { } Enums.Parse("Local") 

Si lo desea, puede dar a Enums un constructor privado y una clase heredada abstracta anidada pública con Temp como Enum , para evitar versiones heredadas para no enumerados.

Tenga en cuenta que no puede usar este truco para hacer métodos de extensión.

 public static T GetEnum(this string description) where T : struct { return (T)Enum.Parse(typeof(T), description); } 

¿Responde a tu pregunta?

IL Weaving using ExtraConstraints

Tu codigo

 public static T GetEnum<[EnumConstraint] T>(this string description) { ... } 

Lo que se comstack

 public static T GetEnum(this string description) where T : Enum { ... } 

Aquí hay una versión de VB.NET de excelente truco de SLaks , con Imports como “typedef”: (La inferencia tipo funciona como se espera, pero no se pueden obtener métodos de extensión).

 'Base namespace "EnumConstraint" Imports Enums = EnumConstraint.Enums(Of System.Enum) Public NotInheritable Class Enums(Of Temp As Class) Private Sub New() End Sub Public Shared Function Parse(Of TEnum As {Temp, Structure})(ByVal Name As String) As TEnum Return DirectCast([Enum].Parse(GetType(TEnum), Name), TEnum) End Function Public Shared Function IsDefined(Of TEnum As {Temp, Structure})(ByVal Value As TEnum) As Boolean Return [Enum].IsDefined(GetType(TEnum), Value) End Function Public Shared Function HasFlags(Of TEnum As {Temp, Structure})(ByVal Value As TEnum, ByVal Flags As TEnum) As Boolean Dim flags64 As Long = Convert.ToInt64(Flags) Return (Convert.ToInt64(Value) And flags64) = flags64 End Function End Class Module Module1 Sub Main() Dim k = Enums.Parse(Of DateTimeKind)("Local") Console.WriteLine("{0} = {1}", k, CInt(k)) Console.WriteLine("IsDefined({0}) = {1}", k, Enums.IsDefined(k)) k = DirectCast(k * 2, DateTimeKind) Console.WriteLine("IsDefined({0}) = {1}", k, Enums.IsDefined(k)) Console.WriteLine(" {0} same as {1} Or {2}: {3} ", IO.FileAccess.ReadWrite, IO.FileAccess.Read, IO.FileAccess.Write, _ Enums.HasFlags(IO.FileAccess.ReadWrite, IO.FileAccess.Read Or IO.FileAccess.Write)) ' These fail to compile as expected: 'Console.WriteLine(Enums.HasFlags(IO.FileAccess.ReadWrite, IO.FileOptions.RandomAccess)) 'Console.WriteLine(Enums.HasFlags(Of IO.FileAccess)(IO.FileAccess.ReadWrite, IO.FileOptions.RandomAccess)) If Debugger.IsAttached Then _ Console.ReadLine() End Sub End Module 

Salida:

 Local = 2 IsDefined(Local) = True IsDefined(4) = False ReadWrite same as Read Or Write: True 

Una cosa peculiar aquí es que hay un buen número de métodos Enum generics que quizás desee escribir cuya implementación depende del tipo “base” de la enumeración.

Por el tipo “base” de una enumeración, E , me refiero al tipo en el espacio de nombres del System cuyo nombre es el mismo que el nombre del miembro de la enumeración System.TypeCode obtenida llamando a System.Type.GetTypeCode(System.Type) para el tipo E Si la enumeración fue declarada en C #, este es el mismo tipo que fue declarado para “heredar” (no estoy seguro de lo que oficialmente se llama en la especificación). Por ejemplo, el tipo de base de la enumeración de Animal continuación es System.Byte :

 public enum Animal : byte { Moose, Squirrel } 

Es posible escribir dichos métodos usando instrucciones de conmutación, pero seguro que es feo, no se pueden obtener parámetros fuertemente tipados o tipos de devolución cuyo tipo es el tipo base de la enumeración, y hay que repetir la búsqueda de metadatos o hacer un poco de almacenamiento en caché (por ejemplo, en el constructor estático para el tipo genérico que contiene el método).