web-dev-qa-db-ja.com

ジェネリッククラスのネストされた列挙からリフレクションを介して列挙値を取得する

リフレクションを通じて取得した特定のタイプの列挙値とそれに対応するアンダーリング値を出力する必要があります。これはほとんどの場合うまくいきます。ただし、enumがジェネリック型内で宣言されている場合、Enum.GetValuesは、次の例外をスローします。

[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()

再生用の完全なコード:

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
    }
}

またはテスト here

これは望ましい動作ですか、どのように対処しますか?

型を作成することは回避策ですが、複雑になりすぎて受け入れられません。

34
CSharpie

型の構築は回避策ですが、あまりにも複雑になるため、私には受け入れられません。

これが、通常の動作をする値を取得する唯一の方法です。

オープン型のフィールドを取得できますが、奇妙なことはcan列挙型の値を取得することです。これらの値を使用しないようにしてください。ただし、それらの基になる型に変換できます。

_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}");
   }
}
_

ただし、より良い解決策は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()}");
   }
}
_

そうすれば、このような奇妙な値が生成されないようにCLRが修正されていれば、コードは壊れません。

44
Jon Skeet

これは予想される動作です。実行時にオープンジェネリック型は存在できないため、それらの内部に存在するものも存在できません。これを行う唯一の方法は、最初に任意のタイプの親タイプを閉じてから、それを使用して列挙型を反映することです。

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

Fooはオープン型(ジェネリックが含まれているため完全には定義されていない型)と呼ばれるものです。また、オープン型の配列は許可されていません。

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

また、EnumのGetValuesは配列の作成に依存しているため、失敗します。代わりにできます

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

(「オブジェクト」はダミータイプなので、オープンタイプでは動作しません)またはJon Skeetが提案したことを実行します。

2
areller