如何将IEnumerable <t>转换为字符串,递归?</t>

时间:2013-04-07 03:17:49

标签: c# generics

我想要一个我可以调用的函数来替代.ToString(),它将显示集合的内容。

我试过这个:

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();
}

但第二次重载永远不会被调用。例如:

List<string> list = new List<string>();
list.Add("polo");
Dictionary<int, List<string>> dict;
dict.Add(1, list);
Console.WriteLine(dump(dict));

我期待这个输出:

{1=>{"polo", }, }

实际发生的是: dict被正确解释为IEnumerable<KeyValuePair<int, List<string>>>,因此调用第3次重载。

第3次重载调用KeyValuePair&gt;上的转储。这应该(?)调用第二个重载,但它没有 - 它调用第一个重载。

所以我们得到了这个输出:

{[1=>System.Collections.Generic.List`1[System.String]], }

是根据KeyValuePair的.ToString()方法构建的。

为什么没有调用第二个重载?在我看来,运行时应该具有识别具有完整通用参数的KeyValuePair所需的所有信息,并调用该参数。

3 个答案:

答案 0 :(得分:5)

泛型是一个编译时概念,而不是运行时。 换句话说,类型参数在编译时解析。

在你的foreach中你打电话给dump(t)而t是T型。 但是除了它是Object之外,目前还没有人知道T。 这就是调用第一个重载的原因。

答案 1 :(得分:2)

(已更新)正如其他答案中所提到的,问题是编译器不知道V类型实际上是List<string>,所以它只是{ {1}}。

可能的解决方法可能是在运行时检查类型。 dump(object)会告诉您变量的类型是否具有泛型,Type.IsGenericType将为您提供这些泛型的实际类型。

因此,您可以编写一个接收对象并忽略任何泛型信息的Type.GetGenericArguments方法。请注意,我使用dump界面而不是System.Collections.IEnumerable

System.Collections.Generics.IEnumerable<T>

即:a)迭代集合,b)keyvaluepair显示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(); } ,c)任何其他对象只调用key => value。使用此代码

ToString

你得到了预期的输出:

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));

答案 2 :(得分:0)

要在foreach中调用第二个版本,您需要指定模板参数KV,否则它将始终调用第一个版本:

dump(t); // always calls first version
dump<K,V>(t); // will call the second

如何获取参数类型KV是另一个问题....