简体   繁体   中英

Iterating over dictionary obtained through reflection

So I have this code which is supposed to recursively print all the properties and their content of a given object.

static public void PrintProperties(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)
    {
        object propValue = property.GetValue(obj, null);
        if (property.PropertyType.Assembly == objType.Assembly && !property.PropertyType.IsEnum)
        {
            Console.WriteLine("{0}{1}:", indentString, property.Name);
            PrintProperties(propValue, indent + 2);
        }
        else
        {
            if (null != propValue)
            {
                Type t = propValue.GetType();
                //Console.WriteLine(":::::{0}:::::", propValue.GetType());
                bool isDict = t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dictionary<,>);
                if (isDict)
                {
                    Type keyType = t.GetGenericArguments()[0];
                    Type valueType = t.GetGenericArguments()[1];
                    foreach (KeyValuePair<keyType, valueType> kvp in (Dictionary<keyType, valueType>)propValue)
                    {
                        Console.WriteLine(string.Format("Key = {0}, Value = {1}", kvp.Key, kvp.Value));
                    }
                }
            }

            Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
        }
    }
}

It doesn't work for List and Dictionary yet, I'm working on the Dictionary part right now.

Problem is, I extract the type of the key and value with:

Type keyType = t.GetGenericArguments()[0];
Type valueType = t.GetGenericArguments()[1];

But then VS2013 tells me that there is a problem with this line:

foreach (KeyValuePair<keyType, valueType> kvp in (Dictionary<keyType, valueType>)propValue)

It tells me that the type or namespace KeyType and valueType are not found. What am I missing?

Thanks.

PS : .net 4.5.1

在调用通用方法时,您必须提供实际的类型名称(或从您自己的方法的定义中传递通用类型参数),而不是Type实例。

Basically your keyType & valueType are variables of type Type , which are known at runtime, so you'd have to use reflection to cast your propValue to an appropriate generic Dictionary. You can however use the fact that, for backwards compatibility, Dictionary<TKey,TValue> implements the non generic IDictionary interface.

So in your specific case, it would be enough to replace this:

foreach (KeyValuePair<keyType, valueType> kvp in (Dictionary<keyType, valueType>)propValue)

with this:

foreach (DictionaryEntry kvp in (IDictionary)propValue)

As decPL indicated, you should use System.Collections.IDictionary. However you should also change your isDict logic. System.Type.IsAssignableFrom allows you to check if "an instance of a specified type can be assigned to the current type instance". Here is code showing it's behavior.

using System;
using System.Collections;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Testing System.Type.IsAssignableFrom");

            var propValue = new Dictionary<string, string>() { { "hello", "world" } };
            var t = propValue.GetType();
            bool isDict = typeof(IDictionary).IsAssignableFrom(t);
            if (isDict)
            {
                foreach (DictionaryEntry kvp in (IDictionary)propValue)
                {
                    Console.WriteLine(string.Format("Key = {0}, Value = {1}", kvp.Key, kvp.Value));
                }
            }

            Console.ReadLine();
        }
    }
}

So your method should look like this.

static public void PrintProperties(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)
    {
        object propValue = property.GetValue(obj, null);
        if (property.PropertyType.Assembly == objType.Assembly && !property.PropertyType.IsEnum)
        {
            Console.WriteLine("{0}{1}:", indentString, property.Name);
            PrintProperties(propValue, indent + 2);
        }
        else
        {
            if (null != propValue)
            {
                Type t = propValue.GetType();
                //Console.WriteLine(":::::{0}:::::", propValue.GetType());
                bool isDict = typeof(IDictionary).IsAssignableFrom(t);
                if (isDict)
                {
                    foreach (DictionaryEntry kvp in (IDictionary)propValue)
                    {
                        Console.WriteLine(string.Format("Key = {0}, Value = {1}", kvp.Key, kvp.Value));
                    }
                }
            }

            Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
        }
    }
}

However you could also do this with the C# as operator. The as operator will attempt to do a cast to the specified type. If it is not possible, it returns null. Some sample code.

using System;
using System.Collections;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Testing System.Type.IsAssignableFrom");

            var propValue = new Dictionary<string, string>() { { "hello", "world" } };
            IDictionary asDict = propValue as IDictionary;
            if (asDict != null)
            {
                foreach (DictionaryEntry kvp in (IDictionary)propValue)
                {
                    Console.WriteLine(string.Format("Key = {0}, Value = {1}", kvp.Key, kvp.Value));
                }
            }

            Console.ReadLine();
        }
    }
}

And your method.

static public void PrintProperties(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)
    {
        object propValue = property.GetValue(obj, null);
        if (property.PropertyType.Assembly == objType.Assembly && !property.PropertyType.IsEnum)
        {
            Console.WriteLine("{0}{1}:", indentString, property.Name);
            PrintProperties(propValue, indent + 2);
        }
        else
        {
            if (null != propValue)
            {
                var asDict = propValue as IDictionary;
                if (asDict != null)
                {
                    foreach (DictionaryEntry kvp in asDict)
                    {
                        Console.WriteLine(string.Format("Key = {0}, Value = {1}", kvp.Key, kvp.Value));
                    }
                }
            }

            Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM