web-dev-qa-db-fra.com

Contraintes de type Enum en C #

Duplicate possible:
Quelqu'un connait-il une bonne solution de contournement pour l'absence de contrainte générique enum?

Quelle est la raison pour laquelle C # n'autorise pas les contraintes de type sur Enum? Je suis sûr qu'il existe une méthode derrière la folie, mais j'aimerais comprendre pourquoi ce n'est pas possible.

Vous trouverez ci-dessous ce que j'aimerais pouvoir faire (en théorie).

public static T GetEnum<T>(this string description) where T : Enum
{
...
}
149
Taylor Leese

C'est une fonctionnalité occasionnellement demandée.

Comme je me plais à le souligner, TOUTES les fonctionnalités sont non implémentées jusqu'à ce que quelqu'un conçoive, spécifie, implémente, teste, documente et expédie la fonctionnalité. Jusqu'à présent, personne ne l'a fait pour celui-ci. Il n'y a pas de raison particulièrement inhabituelle pourquoi pas; nous avons beaucoup d'autres choses à faire, des budgets limités, et celui-ci n'a jamais dépassé le stade "ne serait-ce pas agréable?" discussion au sein de l'équipe de conception linguistique.

Le CLR ne le supporte pas, donc pour que cela fonctionne, il faudrait que nous travaillions à l'exécution en plus du travail sur les langues. (voir les commentaires de réponse)

Je peux voir qu’il existe quelques cas d’utilisation décents, mais aucun d’entre eux n’est si convaincant que nous ferions ce travail plutôt que l’une des centaines d’autres fonctionnalités qui sont beaucoup plus demandées, ou qui sont plus convaincantes et plus étendues. cas d'utilisation. (Si nous allons nous tromper avec ce code, je donnerais personnellement la priorité aux contraintes de délégué, bien au-dessus des contraintes d'énum.)

92
Eric Lippert

En fait, c'est possible, avec un truc moche. Cependant, il ne peut pas être utilisé pour les méthodes d'extension.

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

Enums.Parse<DateTimeKind>("Local")

Si vous le souhaitez, vous pouvez donner Enums<Temp> _ un constructeur privé et une classe héritée abstraite publique imbriquée avec Temp comme Enum, pour empêcher les versions héritées pour les non-énumérations.

Notez que vous ne pouvez pas utiliser cette astuce pour créer des méthodes d'extension.

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

Répond-il à votre question?

15
Andrei Sedoi

IL Tissage avec ExtraConstraints

Votre code

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

Qu'est-ce qui est compilé

public static T GetEnum<T>(this string description) where T : Enum
{
    ...
}
7
Simon

Voici une version VB.NET de SLaks, excellente astuce laide , avec Imports comme "typedef": (L'inférence de type fonctionne comme prévu, mais vous ne pouvez pas obtenir de méthodes d'extension.)

'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

Sortie:

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

Une chose bizarre ici est qu’il existe un assez grand nombre de méthodes génériques Enum que vous pouvez écrire, dont la mise en œuvre dépend du type "base" de l’énumération.

Par le type "base" d'une énumération, E, j'entends le type de l'espace de noms System dont le nom est identique au nom du membre de l'énumération System.TypeCode Obtenue. en appelant System.Type.GetTypeCode(System.Type) pour le type E. Si l'énumération a été déclarée en C #, il s'agit du même type que celui dont elle a été déclarée "hériter" (je ne suis pas sûr de ce qui est officiellement appelé dans la spécification). Par exemple, le type de base de l'énumération Animal ci-dessous est System.Byte:

public enum Animal : byte
{
    Moose,
    Squirrel
}

Il est possible d'écrire de telles méthodes en utilisant des instructions switch, mais c'est moche, vous ne pouvez pas obtenir de paramètres fortement typés ni de types retournés dont le type est le type de base de l'énumération, et vous devez répéter la recherche de métadonnées ou mettre en cache (par exemple dans le constructeur statique pour le type générique contenant la méthode).

2
Doug McClean