如何转换IEnumerable到字符串,递归地
本文关键字:字符串 递归 何转换 转换 IEnumerable | 更新日期: 2023-09-27 18:08:05
我想要一个函数,我可以调用它作为。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>>>
,因此调用第三个重载。
第三个重载调用转储在KeyValuePair>上。这应该(?)调用第二个重载,但它没有——而是调用了第一个重载。
所以我们得到这个输出:
{[1=>System.Collections.Generic.List`1[System.String]], }
由KeyValuePair的。tostring()方法构建。
为什么没有调用第二个重载?在我看来,运行时应该具有识别具有完整泛型参数的KeyValuePair并调用该参数所需的所有信息。
泛型是编译时的概念,而不是运行时的概念。换句话说,类型参数在编译时解析。
在你的foreach中你调用dump(t)
, t是t型的。但是对于这一点上的T,除了知道它是一个Object
外,什么都不知道。这就是为什么要调用第一个重载
(updated)正如其他答案中提到的,问题是编译器不知道类型V
实际上是List<string>
,所以它只是转到dump(object)
。
一个可能的解决方法是在运行时检查类型。Type.IsGenericType
将告诉您变量的类型是否具有泛型,Type.GetGenericArguments
将告诉您这些泛型的实际类型。
因此,您可以编写单个dump
方法来接收对象并忽略任何泛型信息。注意,我使用的是System.Collections.IEnumerable
接口,而不是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();
}
即:a)一个集合迭代,b)一个keyvaluepair显示key => value
, c)任何其他对象只调用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));
你会得到预期的输出:
{marco, }
1 => {marco, }
{1 => {marco, }, }
要在foreach
中调用第二个版本,您需要指定模板参数K
和V
,否则它将始终调用第一个版本:
dump(t); // always calls first version
dump<K,V>(t); // will call the second
如何获得参数类型K
和V
是另一个问题....