35 votes

Obtenir les valeurs d'énum en réfléchissant à partir de l'énumération imbriquée dans la classe générique

J'ai besoin d'imprimer les valeurs de l'enum et de leurs correspondants underyling valeurs de certains types je accquire par la réflexion. Cela fonctionne bien la plupart du temps. Toutefois, si l'enum est déclarée à l'intérieur d'un type générique, Enum.GetValues jette l'exception suivante:

[System.NotSupportedException: Cannot create arrays of open type. ]  
at System.Array.InternalCreate(Void* elementType, Int32 rank, Int32* pLengths, Int32* pLowerBounds)    
at System.Array.CreateInstance(Type elementType, Int32 length)
at System.Array.UnsafeCreateInstance(Type elementType, Int32 length)   
at System.RuntimeType.GetEnumValues()

Code complet pour la reproduction :

using System;

public class Program
{
    public static void Main()
    {
       var enumType= typeof(Foo<>.Bar);
       var underlyingType = Enum.GetUnderlyingType(enumType);
       Console.WriteLine(enumType.IsEnum);

       foreach(var value in Enum.GetValues(enumType))
       {
           Console.WriteLine("{0} = {1}", value, Convert.ChangeType(value, underlyingType));
       }
    }

}

public class Foo<T>
{
    public enum Bar
    {
        A = 1,
        B = 2
    }
}

Ou test ici

Est-ce le comportement souhaité et comment dois-je travailler autour?

La construction d'un type serait un workarround mais inacceptable pour moi, car il serait trop compliqué.

45voto

Jon Skeet Points 692016

La Construction d'un type serait une solution de contournement, mais inacceptable pour moi, car il serait trop compliqué.

C'est la seule façon d'obtenir des valeurs qui se comportent normalement.

Vous pouvez obtenir les champs de type ouvert, et le chose de bizarre, c'est que vous pouvez obtenir les valeurs de cette façon pour les énumérations. Vous devriez essayer d'éviter d'utiliser ces valeurs, mais vous pouvez les convertir à leur type sous-jacent.

public static void Main()
{
   var enumType = typeof(Foo<>.Bar);
   var underlyingType = Enum.GetUnderlyingType(enumType);

   foreach(var field in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
   {
       var value = field.GetValue(null);
       var underlyingValue = Convert.ChangeType(value, underlyingType);
       Console.WriteLine($"{field.Name} = {underlyingValue}");
   }
}

Cependant, une meilleure solution est d'utiliser field.GetRawConstantValue():

public static void Main()
{
   var enumType = typeof(Foo<>.Bar);

   foreach(var field in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
   {
       Console.WriteLine($"{field.Name} = {field.GetRawConstantValue()}");
   }
}

De cette façon, si le CLR est jamais fixé à prévenir de telles valeurs étranges dans un premier temps, votre code ne se cassera pas.

3voto

Akos Nagy Points 2989

C'est le comportement attendu. Les types génériques ouverts ne peuvent pas exister au moment de l'exécution, pas plus que tout ce qui y vit. La seule façon de procéder consiste à fermer le type parent avec n'importe quel type, puis à l'utiliser pour refléter l'énumération suivante:

  var enumType = typeof(Foo<object>.Bar);
 

2voto

areller Points 1607

Foo est ce qu'on appelle un type ouvert (un type qui n'est pas complètement défini car il contient un générique). Et un tableau de type ouvert n'est pas autorisé, vous pouvez le simuler en faisant

 Array.CreateInstance(typeof(Foo<>), 2)
 

Et puisque GetValues of Enum dépend de la création d'un tableau, il échoue. Vous pourriez plutôt faire

 var enumType = typeof(Foo<object>.Bar);
 

("objet" étant un type factice afin de ne pas travailler avec un type ouvert) ou faire ce que Jon Skeet a suggéré.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X