How do I convert IEnumerable<T> to string, recursively?
c#, generics
Solution
(updated) As mentioned in other answers, the problem is that the compiler does not know that type `V` is actually a `List<string>`, so it just goes to `dump(object)`.
A possible workaround might be to check types at run time. `Type.IsGenericType` will tell you if the type of a variable has generics or not, and `Type.GetGenericArguments` will give you the actual type of those generics.
So you can write a single `dump` method receiving an object and ignoring any generics info. Note that I use the `System.Collections.IEnumerable` interface rather than `System.Collections.Generics.IEnumerable<T>`.
public static string dump(Object o)
{
Type type = o.GetType();
// if it's a generic, check if it's a collection or keyvaluepair
if (type.IsGenericType) {
// a collection? iterate items
if (o is System.Collections.IEnumerable) {
StringBuilder result = new StringBuilder("{");
foreach (var i in (o as System.Collections.IEnumerable)) {
result.Append(dump(i));
result.Append(", ");
}
result.Append("}");
return result.ToString();
// a keyvaluepair? show key => value
} else if (type.GetGenericArguments().Length == 2 &&
type.FullName.StartsWith("System.Collections.Generic.KeyValuePair")) {
StringBuilder result = new StringBuilder();
result.Append(dump(type.GetProperty("Key").GetValue(o, null)));
result.Append(" => ");
result.Append(dump(type.GetProperty("Value").GetValue(o, null)));
return result.ToString();
}
}
// arbitrary generic or not generic
return o.ToString();
}
That is: a) a collection is iterated, b) a keyvaluepair shows `key => value`, c) any other object just calls `ToString`. With this code
List<string> list = new List<string>();
list.Add("polo");
Dictionary<int, List<string>> dict = new Dictionary<int, List<string>>() ;
dict.Add(1, list);
Console.WriteLine(dump(list));
Console.WriteLine(dump(dict.First()));
Console.WriteLine(dump(dict));
you get the expected output:
{marco, }
1 => {marco, }
{1 => {marco, }, }
Problem
I want a function that I can call as an alternative to .ToString(), that will show the contents of collections. I've tried this: ``` public static string dump(Object o) { if (o == null) return "null"; return o.ToString(); } public static string dump<K, V>(KeyValuePair<K, V> kv) { return dump(kv.Key) + "=>" + dump(kv.Value); } public static string dump<T>(IEnumerable<T> list) { StringBuilder result = new StringBuilder("{"); foreach(T t in list) { result.Append(dump(t)); result.Append(", "); } result.Append("}"); return result.ToString(); } ``` but the second overload never gets called. For example: ``` List<string> list = new List<string>(); list.Add("polo"); Dictionary<int, List<string>> dict; dict.Add(1, list); Console.WriteLine(dump(dict)); ``` I'm expecting this output: ``` {1=>{"polo", }, } ``` What actually happens is this: dict is correctly interpreted as an `IEnumerable<KeyValuePair<int, List<string>>>`, so the 3rd overload is called. the 3rd overload calls dump on a KeyValuePair>. This should(?) invoke the second overload, but it doesn't -- it calls the first overload instead. So we get this output: ``` {[1=>System.Collections.Generic.List`1[System.String]], } ``` which is built from KeyValuePair's .ToString() method. Why isn't the second overload called? It seems to me that the runtime should have all the information it needs to identify a KeyValuePair with full generic arguments and call that one.