web-dev-qa-db-ja.com

リフレクションを介したコレクションへのアクセス

リフレクションを使用してコレクションを(できればforeachを介して)反復する方法はありますか?リフレクションを使用してオブジェクトのプロパティを反復処理しています。プログラムがコレクションである型に到達したら、コレクションのコンテンツを反復処理して、コレクション内のオブジェクトにアクセスできるようにしたいと思います。

現在、すべてのプロパティに属性が設定されており、コレクションであるプロパティのIsCollectionフラグがtrueに設定されています。私のコードはこのフラグをチェックし、それがtrueの場合、リフレクションを使用してTypeを取得します。コレクションでGetEnumeratorまたはItemsを何らかの方法で呼び出して、アイテムを反復処理できるようにする方法はありますか?

29
Bob Wintemberg

この問題が発生しましたが、リフレクションを使用する代わりに、IEnumerableであるかどうかを確認するだけで済みました。すべてのコレクションがそれを実装しています。

if (item is IEnumerable)
{
    foreach (object o in (item as IEnumerable))
    {

    }
} else {
   // reflect over item
}
35
Darren Kopp

ダレンが提案したのと同様の手法を使用しようとしましたが、コレクションだけがIEnumerableを実装しているわけではないことに注意してください。たとえば、stringもIEnumerableであり、文字を繰り返し処理します。

これは、オブジェクトがコレクションであるかどうかを判断するために使用している小さな関数です(ICollectionもIEnumerableであるため、これも列挙可能です)。

    public bool isCollection(object o)
    {
        return typeof(ICollection).IsAssignableFrom(o.GetType())
            || typeof(ICollection<>).IsAssignableFrom(o.GetType());
    }
29
Nagyman

プロパティの値を取得して、それをIEnumerableにキャストするだけです。これがあなたにアイデアを与えるためのいくつかの(テストされていない)コードです:

ClassWithListProperty obj = new ClassWithListProperty();
obj.List.Add(1);
obj.List.Add(2);
obj.List.Add(3);

Type type = obj.GetType();
PropertyInfo listProperty = type.GetProperty("List", BindingFlags.Public);
IEnumerable listObject = (IEnumerable) listProperty.GetValue(obj, null);

foreach (int i in listObject)
    Console.Write(i); // should print out 123
8
jop

情報のためだけに、誰かの助けになるかもしれません...ネストされたクラスと他のいくつかのクラスのコレクションを持つクラスがありました。クラスのプロパティ値、ネストされたクラス、およびクラスのコレクションを保存したかったのです。私のコードは次のとおりです。

 public void LogObject(object obj, int indent)
    {
        if (obj == null) return;
        string indentString = new string(' ', indent);
        Type objType = obj.GetType();
        PropertyInfo[] properties = objType.GetProperties();

        foreach (PropertyInfo property in properties)
        {
            Type tColl = typeof(ICollection<>);
            Type t = property.PropertyType;
            string name = property.Name;


            object propValue = property.GetValue(obj, null); 
            //check for nested classes as properties
            if (property.PropertyType.Assembly == objType.Assembly)
            {
                string _result = string.Format("{0}{1}:", indentString, property.Name);
                log.Info(_result);
                LogObject(propValue, indent + 2);
            }
            else
            {
                string _result = string.Format("{0}{1}: {2}", indentString, property.Name, propValue);
                log.Info(_result);
            }

            //check for collection
            if (t.IsGenericType && tColl.IsAssignableFrom(t.GetGenericTypeDefinition()) ||
                t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == tColl))
            {
                //var get = property.GetGetMethod();
                IEnumerable listObject = (IEnumerable)property.GetValue(obj, null);
                if (listObject != null)
                {
                    foreach (object o in listObject)
                    {
                        LogObject(o, indent + 2);
                    }
                }
            }
        }
    }

この関数と呼ばれる

LogObject(obj, 0);

ただし、クラス内にいくつかの構造体があり、それらの値を取得する方法を理解する必要があります。 Moreoevr、私はいくつかのリストを持っています。私もそれらの値を取得する必要があります....コードを更新したら投稿します。

2
shazia

おそらくあなたができる最善のことは、オブジェクトが特定のコレクションインターフェイスを実装しているかどうかを確認することです-おそらくIEnumerableが必要なすべてです。次に、オブジェクトからGetEnumerator()を呼び出し、IEnumerator.MoveNext()とIEnumerator.Currentを使用してコレクションを処理するだけです。

コレクションがそれらのインターフェースを実装していない場合、これは役に立ちませんが、その場合、それは実際にはコレクションの多くではないと思います。

2
Andy

Type.FindInterfacesメソッドを見てみましょう。これにより、特定のタイプによって実装されたインターフェースを除外できます。 PropertyInfo.PropertyType.FindInterfaces(filterMethod、filterObjects)のように。 IEnumerableでフィルタリングして、結果が返されるかどうかを確認できます。 MSDNには、メソッドのドキュメントに優れた例があります。

0
Eric Swann

オブジェクトのインスタンスではなくタイプを使用している場合は、次を使用できます。

// type is IEnumerable
if (type.GetInterface("IEnumerable") != null)
{
}
0
Abdo

リフレクションを使用する場合、必ずしもそのオブジェクトのインスタンスを使用しているとは限りません。オブジェクトのプロパティを反復処理できる、そのタイプのインスタンスを作成する必要があります。したがって、リフレクションを使用している場合は、ConstructorInfo.Invoke()(?)メソッドを使用して、新しいインスタンスを作成するか、タイプのインスタンスをポイントします。

0
Adam Driscoll

かなり簡単なアプローチは、オブジェクトをコレクションとして型キャストし、それを直接使用することです。

0
nullDev