リフレクションを使用してコレクションを(できればforeachを介して)反復する方法はありますか?リフレクションを使用してオブジェクトのプロパティを反復処理しています。プログラムがコレクションである型に到達したら、コレクションのコンテンツを反復処理して、コレクション内のオブジェクトにアクセスできるようにしたいと思います。
現在、すべてのプロパティに属性が設定されており、コレクションであるプロパティのIsCollectionフラグがtrueに設定されています。私のコードはこのフラグをチェックし、それがtrueの場合、リフレクションを使用してTypeを取得します。コレクションでGetEnumeratorまたはItemsを何らかの方法で呼び出して、アイテムを反復処理できるようにする方法はありますか?
この問題が発生しましたが、リフレクションを使用する代わりに、IEnumerableであるかどうかを確認するだけで済みました。すべてのコレクションがそれを実装しています。
if (item is IEnumerable)
{
foreach (object o in (item as IEnumerable))
{
}
} else {
// reflect over item
}
ダレンが提案したのと同様の手法を使用しようとしましたが、コレクションだけがIEnumerableを実装しているわけではないことに注意してください。たとえば、string
もIEnumerableであり、文字を繰り返し処理します。
これは、オブジェクトがコレクションであるかどうかを判断するために使用している小さな関数です(ICollectionもIEnumerableであるため、これも列挙可能です)。
public bool isCollection(object o)
{
return typeof(ICollection).IsAssignableFrom(o.GetType())
|| typeof(ICollection<>).IsAssignableFrom(o.GetType());
}
プロパティの値を取得して、それを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
情報のためだけに、誰かの助けになるかもしれません...ネストされたクラスと他のいくつかのクラスのコレクションを持つクラスがありました。クラスのプロパティ値、ネストされたクラス、およびクラスのコレクションを保存したかったのです。私のコードは次のとおりです。
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、私はいくつかのリストを持っています。私もそれらの値を取得する必要があります....コードを更新したら投稿します。
おそらくあなたができる最善のことは、オブジェクトが特定のコレクションインターフェイスを実装しているかどうかを確認することです-おそらくIEnumerableが必要なすべてです。次に、オブジェクトからGetEnumerator()を呼び出し、IEnumerator.MoveNext()とIEnumerator.Currentを使用してコレクションを処理するだけです。
コレクションがそれらのインターフェースを実装していない場合、これは役に立ちませんが、その場合、それは実際にはコレクションの多くではないと思います。
Type.FindInterfacesメソッドを見てみましょう。これにより、特定のタイプによって実装されたインターフェースを除外できます。 PropertyInfo.PropertyType.FindInterfaces(filterMethod、filterObjects)のように。 IEnumerableでフィルタリングして、結果が返されるかどうかを確認できます。 MSDNには、メソッドのドキュメントに優れた例があります。
オブジェクトのインスタンスではなくタイプを使用している場合は、次を使用できます。
// type is IEnumerable
if (type.GetInterface("IEnumerable") != null)
{
}
リフレクションを使用する場合、必ずしもそのオブジェクトのインスタンスを使用しているとは限りません。オブジェクトのプロパティを反復処理できる、そのタイプのインスタンスを作成する必要があります。したがって、リフレクションを使用している場合は、ConstructorInfo.Invoke()(?)メソッドを使用して、新しいインスタンスを作成するか、タイプのインスタンスをポイントします。
かなり簡単なアプローチは、オブジェクトをコレクションとして型キャストし、それを直接使用することです。