1

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

user622505
  • 743
  • 6
  • 23
sliders_alpha
  • 2,276
  • 4
  • 33
  • 52
  • `keyType` & `valueType` in your example are instances of `Type` but `KeyValuePair` requires a type name so that the type can be evaluated at runtime. You'll have to access the dictionary using the non-generic `Dictionary` class and cast the key & value objects manually. – Howwie Oct 28 '16 at 12:08
  • 1
    Possible duplicate of [What is the best way to dump entire objects to a log in C#?](http://stackoverflow.com/questions/360277/what-is-the-best-way-to-dump-entire-objects-to-a-log-in-c) – Michael Freidgeim Oct 28 '16 at 12:34

3 Answers3

2

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)
decPL
  • 5,384
  • 1
  • 26
  • 36
1

In calls to generic methods you must provide actual type names (or pass generic type parameters from your own method's definition) - not instances of Type.

user622505
  • 743
  • 6
  • 23
  • So there is no way to automatically print the content of a dictionnary extracted through reflection? because in `propValue` I can see the type of both the key and value (example : `System.Collections.Generic.Dictionary 2[Mobile.Fonct ionComptage.Modele.IndexFournisseur, System.String]`) – sliders_alpha Oct 28 '16 at 12:22
1

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);
        }
    }
}
Jeffrey Patterson
  • 2,342
  • 1
  • 13
  • 9