C# 如何转换IEnumerable<;T>;以递归方式创建字符串?

C# 如何转换IEnumerable<;T>;以递归方式创建字符串?,c#,generics,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) + "=>

我想要一个可以调用的函数,作为.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();
}
实际情况是: dict被正确地解释为
IEnumerable
,因此调用第三个重载

第三个重载调用KeyValuePair>上的dump。这应该(?)调用第二个重载,但它不调用——它调用第一个重载

所以我们得到这个输出:

{1=>{"polo", }, }
{[1=>System.Collections.Generic.List`1[System.String]], }
它是从KeyValuePair的.ToString()方法构建的


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

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

dump(t); // always calls first version
dump<K,V>(t); // will call the second
dump(t);//总是调用第一个版本
转储(t);//我会叫第二个

如何获得参数类型
K
V
是另一个问题。

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

在foreach中,您调用
dump(t)
,t的类型为t。 但在这一点上,除了T是一个
对象之外,对T一无所知。
这就是调用第一个重载的原因。

(更新)正如其他答案中提到的,问题是编译器不知道类型
V
实际上是一个
列表,所以它只会转到
转储(对象)

一种可能的解决方法可能是在运行时检查类型
Type.IsGenericType
将告诉您变量的类型是否具有泛型,而
Type.GetGenericArguments
将提供这些泛型的实际类型

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

也就是说: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));

不是重复的,但可能很有趣:可能与
KeyValuePair
是一个结构而不是一个类有关。问题不在于没有为
列表调用第三个重载吗?您确实可以从第二个重载获得输出,但是当它转储该对时,它使用第一个重载而不是第三个重载。你能试着只写
dump(list)
?另外:您是否已调试以验证所做的决策是否正确?逐步完成代码,然后变得更聪明!=)<代码>转储(列表)
工作正常<代码>转储(dict.First())
正确地转到第二个
转储
,但是当转储值时,它转到第一个转储。这里没有递归。这将导致编译器错误,因为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();
}
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, }, }