7

我想要一个可以作为 .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 并调用该参数所需的所有信息。

4

3 回答 3

5

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

在您的 foreach 中,您调用dump(t)并且 t 是 T 类型。但是此时除了 T 是一个 之外,对 T 一无所知Object。这就是调用第一个重载的原因。

于 2013-04-07T03:43:09.100 回答
2

(更新)正如其他答案中提到的,问题是编译器不知道类型V实际上是 a 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)一个键值对显示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, }, }
于 2013-04-07T04:42:03.383 回答
0

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

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

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

于 2013-04-07T03:37:28.340 回答