Programmation DotNet : Comment faire une énumération de type String ?
Le moyen le plus simple de faire une énumération de type String en DotNet est la suivante (exemple en VB .Net) :
Public Module enumOuiNon Public Nul$ = "Nul" Public Oui$ = "Oui" Public Non$ = "Non" End Module Dim maVar$ = enumOuiNon.Oui Debug.WriteLine(maVar)
Ou bien alors, sans oublier le Const cette fois :
Public Class enumOuiNon Public Const Nul$ = "Nul" Public Const Oui$ = "Oui" Public Const Non$ = "Non" End Class
L'inconvénient, c'est que vous pouvez alors mettre n'importe quoi dans votre variable, par exemple :
Dim maVar$ = "Toto va à la plage"
Tout simplement parce que votre énumération a un typage faible : seul le type String est vérifié à la compilation, et non les différentes valeurs permises de l'énumération.
Pour pouvoir restreindre à la compilation les valeurs permises de l'énumération à votre variable, il faut lui appliquer un typage fort, mais cela requiert d'écrire un peu plus de code :
Public Structure TTypeOuiNon : Implements IComparable
<summary> Le type n'est pas defini </summary> Public Shared ReadOnly Property Nul() As TTypeOuiNon Get Return New TTypeOuiNon(enumOuiNon.Nul) End Get End Property
<summary> Le type vaut Oui </summary> Public Shared ReadOnly Property Oui() As TTypeOuiNon Get Return New TTypeOuiNon(enumOuiNon.Oui) End Get End Property
<summary> Le type vaut Non </summary> Public Shared ReadOnly Property Non() As TTypeOuiNon Get Return New TTypeOuiNon(enumOuiNon.Non) End Get End Property
Private m_sValeur$ Public Sub New(ByVal sValeur$) m_sValeur = sValeur End Sub
Public Overrides Function ToString$() Return m_sValeur End Function
Public Overloads Overrides Function Equals(ByVal obj As Object) As Boolean Dim sValeur$ = DirectCast(obj, TTypeOuiNon).ToString() Dim bEgal As Boolean = (sValeur = m_sValeur) Return bEgal End Function
Public Overloads Function CompareTo(ByVal autre As Object) As Integer _ Implements IComparable.CompareTo ' If other is not a valid object reference, this instance is greater. If IsNothing(autre) Then Return 1 Dim sValeur$ = autre.ToString Return m_sValeur.CompareTo(sValeur) End Function
' Surcharges d'opérateurs Public Shared Operator =(ByVal typeG As TTypeOuiNon, ByVal typeD As TTypeOuiNon) As Boolean Return typeG.Equals(typeD) End Operator
Public Shared Operator <>(ByVal typeG As TTypeOuiNon, ByVal typeD As TTypeOuiNon) As Boolean Return Not typeG.Equals(typeD) End Operator
Public Shared Operator &(ByVal typeG$, ByVal typeD As TTypeOuiNon) As String Return typeG & typeD.ToString End Operator
Public Shared Operator &(ByVal typeG As TTypeOuiNon, ByVal typeD As TTypeOuiNon) As String Return typeG.ToString & typeD.ToString End Operator
Public Function bEstValide() As Boolean
Select Case m_sValeur Case enumOuiNon.Nul, enumOuiNon.Oui, enumOuiNon.Non : Return True Case Else : Return False End Select
End Function
End Structure
Vous pouvez maintenant écrire ceci :
Dim maVarOui As TTypeOuiNon = TTypeOuiNon.Oui Dim maVarNon As TTypeOuiNon = TTypeOuiNon.Non Dim maVarNul As TTypeOuiNon = TTypeOuiNon.Nul 'Dim maVarAutre As TTypeOuiNon = "Toto" ' Ne compile pas Dim maVarAutre As TTypeOuiNon = New TTypeOuiNon("Toto") Dim maVarOui2 = TTypeOuiNon.Oui Dim lst As New List(Of TTypeOuiNon) From {maVarOui, maVarNon, maVarNul, maVarAutre, maVarOui2}
Debug.WriteLine("maVarOui : " & maVarOui) Debug.WriteLine("maVarNon : " & maVarNon) Debug.WriteLine("maVarNul : " & maVarNul) Debug.WriteLine("maVarAutre : " & maVarAutre & ", valide : " & maVarAutre.bEstValide) Debug.WriteLine("maVarOui = maVarNon : " & (maVarOui = maVarNon)) Debug.WriteLine("maVarOui <> maVarNon : " & (maVarOui <> maVarNon)) Debug.WriteLine("maVarOui = maVarOui2 : " & (maVarOui = maVarOui2)) Debug.WriteLine("maVarOui & maVarOui2 : " & (maVarOui & maVarOui2))
Debug.WriteLine("Avant le tri :") For Each ouiNon In lst Debug.WriteLine(ouiNon) Next lst.Sort() Debug.WriteLine("Après le tri :") For Each ouiNon In lst Debug.WriteLine(ouiNon) Next
Résultats :
maVarOui : Oui maVarNon : Non maVarNul : Nul maVarAutre : Toto, valide : False maVarOui = maVarNon : False maVarOui <> maVarNon : True maVarOui = maVarOui2 : True maVarOui & maVarOui2 : OuiOui Avant le tri : Oui Non Nul Toto Oui Après le tri : Non Nul Oui Oui Toto
On pourrait aussi simplifier le code en remplaçant la propriété (Property) directement par :
Public Shared ReadOnly Nul As New TTypeOuiNon(enumOuiNon.Nul)
mais dans ce cas les commentaires (summary), qui permettent l'affichage d'information lorsque l'on promène la souris sur la variable, ne fonctionnent plus (sous Visual Studio 2010, mais cela a été corrigé dans la version 2013).
Ah au fait, on aurait pu aussi tout simplement utiliser un Enum entier classique avec ToString :
Public Enum enumOuiNonEntier Nul Oui Non Non_Défini End Enum
Dim maVarOui3$ = enumOuiNonEntier.Oui.ToString Dim maVarNon3$ = enumOuiNonEntier.Non.ToString Dim maVarNul3$ = enumOuiNonEntier.Nul.ToString Dim maVarNon_Défini$ = enumOuiNonEntier.Non_Défini.ToString Debug.WriteLine("maVarOui3 : " & maVarOui3) Debug.WriteLine("maVarNon3 : " & maVarNon3) Debug.WriteLine("maVarNul3 : " & maVarNul3) Debug.WriteLine("maVarNon_Défini : " & maVarNon_Défini)
Résultats :
maVarOui3 : Oui maVarNon3 : Non maVarNul3 : Nul maVarNon_Défini : Non_Défini
Mais l'inconvénient est qu'on ne peut alors pas utiliser d'espace dans le nom de l'énumération. On pourrait cependant utiliser un attribut :
Public Enum enumOuiNonEntier Nul Oui Non <System.ComponentModel.Description("Non Défini")> _ Non_Défini End Enum
Mais alors la récupération de l'attribut obligerait à écrire du code, que l'on ne pourrait pas insérer dans l'énumération elle-même, ce qui obligerait du coup à refaire une classe, comme indiquée dans la première solution :
Dim type0 = GetType(enumOuiNonEntier) Dim memInfo = type0.GetMember(enumOuiNonEntier.Non_Défini.ToString()) Dim attributes = memInfo(0).GetCustomAttributes(GetType(System.ComponentModel.DescriptionAttribute), False) Dim description = DirectCast(attributes(0), System.ComponentModel.DescriptionAttribute).Description Debug.WriteLine("maVarNon_Défini : " & description)
Résultat :
maVarNon_Défini : Non Défini
Voici tout de même la fonction qui pourrait être utilisée :
Imports System.ComponentModel
Public Function lireDescriptionEnum$(ByVal monEnum As [Enum]) Dim fi As Reflection.FieldInfo = monEnum.GetType().GetField(monEnum.ToString()) Dim aAttr() As DescriptionAttribute = DirectCast( _ fi.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute()) If aAttr.Length > 0 Then Return aAttr(0).Description Else Return monEnum.ToString() End If End Function
Debug.WriteLine(lireDescriptionEnum(enumOuiNonEntier.Non_Défini)) Debug.WriteLine(lireDescriptionEnum(enumOuiNonEntier.Non))
En conclusion, il est intéressant d'utiliser la programmation générique, car chaque classe d'énumération reprend toujours la même structure, voici comment on peut faire :
Option Infer On
Imports System.Reflection ' FieldInfo
Public MustInherit Class EnumGenerique(Of T As {EnumGenerique(Of T), New}) : Implements IComparable
Protected Shared ReadOnly m_afiChamps As FieldInfo() = Nothing Protected m_iValeur% Protected m_sNom$
Shared Sub New() m_afiChamps = GetType(T).GetFields() End Sub
Protected Sub New(iVal As Integer) Me.m_iValeur = iVal End Sub
Protected Sub New(iVal As Integer, sNom$) Me.New(iVal) Me.m_sNom = sNom End Sub
Protected Sub New() End Sub
Public Shared Operator =(t1 As EnumGenerique(Of T), t2 As EnumGenerique(Of T)) As Boolean
If Object.ReferenceEquals(t1, t2) Then Return True If (DirectCast(t1, Object) Is Nothing) OrElse _ (DirectCast(t2, Object) Is Nothing) Then Return False Return t1.m_iValeur = t2.m_iValeur
End Operator
Public Shared Operator <>(t1 As EnumGenerique(Of T), t2 As EnumGenerique(Of T)) As Boolean Return Not (t1 Is t2) End Operator
Public Overrides Function ToString() As String
If m_sNom IsNot Nothing Then Return m_sNom Else Dim property0 = m_afiChamps.FirstOrDefault(Function(f) DirectCast(f.GetValue(Me), T) = Me) If IsNothing(property0) Then Return "" Return property0.Name End If
End Function
Public Overrides Function Equals(obj As Object) As Boolean
If TypeOf obj Is EnumGenerique(Of T) Then Return Me Is DirectCast(obj, EnumGenerique(Of T)) Else Return False End If
End Function
Public Overloads Function CompareTo(ByVal autre As Object) As Integer _ Implements IComparable.CompareTo ' Si l'autre élément n'est pas valide, cette instance est supérieur If IsNothing(autre) Then Return 1 ' Tri texte (tri sur l'intitulé de l'enumération) Dim sValeur$ = autre.ToString Return Me.ToString.CompareTo(sValeur) ' Tri numérique (tri sur la valeur de l'énumération) 'Dim iValeur% = DirectCast(autre, EnumGenerique(Of T)).m_iValeur 'Return m_iValeur.CompareTo(iValeur) End Function
Public Overrides Function GetHashCode() As Integer Return Me.m_iValeur.GetHashCode() End Function
End Class
Public Class Booleen4Etats : Inherits EnumGenerique(Of Booleen4Etats)
Public Shared ReadOnly Nul As Booleen4Etats = 0 Public Shared ReadOnly Oui As Booleen4Etats = 1 Public Shared ReadOnly Non As Booleen4Etats = 2 Public Shared ReadOnly PeutEtre As Booleen4Etats = 4
Public Sub New() MyBase.New() End Sub
Private Sub New(iVal%) MyBase.New(iVal) End Sub
' Opérateur de conversion implicite de Integer vers Booleen4Etats ' http://msdn.microsoft.com/fr-fr/library/z5z9kes2.aspx : C# ' http://msdn.microsoft.com/fr-fr/library/yf7b9sy7.aspx : VB Public Shared Widening Operator CType(iVal%) As Booleen4Etats Return New Booleen4Etats(iVal) End Operator
' Opérateur de conversion implicite de Booleen4Etats vers Integer Public Shared Widening Operator CType(val As Booleen4Etats) As Integer Return val.m_iValeur End Operator
End Class