Vincoli di tipo Enum in C #

Possibile duplicato:
Qualcuno sa una buona soluzione per la mancanza di un vincolo enum generico?

Qual è la ragione per cui C # non consente vincoli di tipo su Enum ? Sono sicuro che c’è un metodo dietro la follia, ma mi piacerebbe capire perché non è ansible.

Di seguito è ciò che vorrei essere in grado di fare (in teoria).

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

Questa è una caratteristica richiesta occasionalmente.

Come mi piace sottolineare, TUTTE le funzionalità non sono implementate fino a quando qualcuno non progetta, specifica, implementa, verifica, documenta e spedisce la funzionalità. Finora, nessuno lo ha fatto per questo. Non c’è una ragione particolarmente insolita perché no; abbiamo un sacco di altre cose da fare, budget limitati, e questo non ha mai superato “non sarebbe bello?” discussione nel team di progettazione linguistica.

Il CLR non lo supporta, quindi per farlo funzionare avremmo bisogno di fare il lavoro di runtime in aggiunta al lavoro linguistico. (vedi i commenti alla risposta)

Vedo che ci sono alcuni casi di utilizzo decenti, ma nessuno di questi è così convincente che faremmo questo lavoro piuttosto che una delle centinaia di altre funzionalità che sono richieste molto più frequentemente o che sono più coinvolgenti e di più ampia portata casi d’uso. (Se dovessimo muck con questo codice, personalmente darei la priorità ai vincoli dei delegati, molto al di sopra dei vincoli enum).

In realtà, è ansible, con un brutto trucco. Tuttavia, non può essere utilizzato per i metodi di estensione.

 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") 

Se lo si desidera, è ansible assegnare a Enums un costruttore privato e una class ereditata astratta nidificata pubblica con Temp as Enum , per impedire versioni ereditate per non enumerati.

Si noti che non è ansible utilizzare questo trucco per rendere i metodi di estensione.

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

Risponde alla tua domanda?

IL Tessitura usando ExtraConstraints

Il tuo codice

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

Cosa viene compilato

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

Ecco una versione VB.NET di SLaks eccellente trucco brutto , con Imports come “typedef”: (L’inferenza di tipo funziona come previsto, ma non è ansible ottenere i metodi di estensione.)

 '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 

Produzione:

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

Una cosa strana è che esiste un buon numero di metodi Enum generici che potresti voler scrivere la cui implementazione dipende dal tipo “base” dell’enumerazione.

Con il tipo “base” di un’enumerazione, E , intendo il tipo nel namespace System cui nome è uguale al nome del membro dell’enumerazione System.TypeCode ottenuta chiamando System.Type.GetTypeCode(System.Type) per il tipo E Se l’enumerazione è stata dichiarata in C #, questo è lo stesso tipo che è stato dichiarato di “ereditare” da (non sono sicuro di ciò che viene chiamato ufficialmente nella specifica). Ad esempio, il tipo di base dell’enumerazione di Animal seguito è System.Byte :

 public enum Animal : byte { Moose, Squirrel } 

È ansible scrivere tali metodi usando le istruzioni switch, ma è sicuramente brutto, non è ansible ottenere parametri fortemente tipizzati o tipi restituiti il ​​cui tipo è il tipo base dell’enumerazione e si deve ripetere la ricerca dei metadati o fare un po ‘di cache (ad es. nel costruttore statico per il tipo generico contenente il metodo).