简体   繁体   English

无法推断通用tostring方法中的类型C#

[英]Can't infer type in generic tostring method C#

I have a custom facilitator method that takes a value, and determines its type. 我有一个自定义辅助方法,它接受一个值,并确定其类型。 If it's a data structure such that it can be enumerated over, then it gets passed to its respective ToString method to be printed out. 如果它是一个可以枚举的数据结构,那么它将被传递到其各自的ToString方法以进行打印。 But if not, then it's builtin .ToString() method is called. 但如果没有,那么它就是内置的.ToString()方法被调用。

public static String GetString<T> (T value)
{
    Type t = typeof(T);
    if (t.IsSubclassOf (typeof(IDictionary)))
        return _GetString_Dictionary(value);
    else if (t.IsSubclassOf (typeof(IEnumerable)))
        return _GetString_Enumerable (value);
    else
        return value.ToString ();
}

However, I'm getting lots of errors related to how the argument types can't be inferred for each call to the other methods. 但是,我遇到了很多错误,这些错误与每次调用其他方法时无法推断参数类型有关。 I currently have no initiative as to how to solve this problem; 我目前没有主动如何解决这个问题; can it even be accomplished? 甚至可以完成吗?

Here are the other toString methods that the above one calls, for reference; 以下是上面调用的其他toString方法,供参考;

private static String _GetString_Dictionary<KT, VT> (Dictionary<KT, VT> d)
{
    string s = "{";
    foreach (var pair in d)
        s += GetString(pair.Key) + " : " + GetString(pair.Value) + ", ";
    return s.Substring (0, s.Length - 2) + "}";
}

private static String _GetString_Enumerable<T> (IEnumerable<T> e)
{
    string s = "[";
    foreach (T i in e)
        s += GetString(i) + ", ";
    return s.Substring(0, s.Length-2) + "]";
}

Edit: Shortened code as per Equiso's Information. 编辑:根据Equiso的信息缩短代码。


As per David Manning's example, I've modified the code to look as such; 按照David Manning的例子,我修改了代码看起来像这样;

public static String GetString<KT, VT> (Dictionary<KT, VT> d)
{
    string s = "{";
    foreach (var pair in d)
        s += GetString(pair.Key) + " : " + GetString(pair.Value) + ", ";
    return s.Substring (0, s.Length - 2) + "}";
}

public static String GetString<T> (IEnumerable<T> e)
{
    string s = "[";
    foreach (T i in e)
        s += GetString(i) + ", ";
    return s.Substring(0, s.Length-2) + "]";
}

public static String GetString<T> (T value)
{
    return value.ToString ();
}

It no longer throws errors. 它不再抛出错误。

Edit: It does not work as intended; 编辑:它不按预期工作; it instead calls the generic .ToString() version of GetString() on every object passed to it, instead of it's more specific overload if it were otherwise applicable. 它反过来调用传递给它的每个对象的通用.ToString()版本的GetString() ,而不是它更具体的重载,如果它适用的话。 Passing a List object to it returns the string System.Collections.Generic.List'1[System.String] . List对象传递给它返回字符串System.Collections.Generic.List'1[System.String] instead of the contents of the list. 而不是列表的内容。

Quick fix for your solution (Cast the value to its type): 快速修复您的解决方案(将值转换为其类型):

public static String GetString<T> (T value)
{
  Type t = typeof(T);
  if (t.IsSubclassOf (typeof(Array)) || t.IsSubclassOf (typeof(IList)))
    return _ToString_List ((List)value);
  else if (t.IsSubclassOf (typeof(IDictionary)))
    return _ToString_Dictionary((Dictionary)value);
  else if (t.IsSubclassOf (typeof(IEnumerable)))
    return _ToString_Enumerable ((IEnumerable)value);
  else
    return value.ToString ();
}

A more elegant solution will be using extension method 更优雅的解决方案是使用扩展方法

public static class ToStringExtension()
{
  publicstatic String MyToString<T> (this T[] a) 
  {
  if (a.Length == 0)
    return "[]";
  string s = "[";
  for (int i=0; i<a.Length - 1; i++)
    s += GetString(a [i]) + ", ";
  return s + a [a.Length - 1] + "]";
  }

  public static String MyToString<KT, VT> (this Dictionary<KT, VT> d)
  {
  string s = "{";
  foreach (var pair in d)
    s += GetString(pair.Key) + " : " + GetString(pair.Value) + ", ";
  return s.Substring (0, s.Length - 2) + "}";
  }

  public static String MyToString<T> (this IEnumerable<T> e)
  {
  string s = "[";
  foreach (T i in e)
    s += GetString(i) + ", ";
  return s.Substring(0, s.Length-2) + "]";
  }
}

To use the extension method: 要使用扩展方法:

var arr = new string[10];
arr.MyToString();

You do not need to use generics here, you can define GetString with the non-generic collection interfaces and the compiler will bind your calls to the most specific one. 您不需要在此处使用泛型,您可以使用非泛型集合接口定义GetString,编译器会将您的调用绑定到最具体的集合。 For example: 例如:

public static String GetString(object value) {

    if (value is string) {
        return value as string;
    } else if (value is IDictionary) {
        return GetString(value as IDictionary);
    } else if (value is IEnumerable) {
        return GetString(value as IEnumerable);
    } else {
        return value.ToString();
    }

}

public static String GetString(string l) {
    return l;
}

public static String GetString(IEnumerable l) {
    string s = "[";
    foreach (object i in l) {
        s += GetString(i) + ", ";
    }
    if (s != "[") s = s.Substring(0, s.Length - 2);
    return s + "]";
}

public static String GetString(IDictionary d) {
    string s = "{";
    foreach (object key in d.Keys) {
        s += GetString(key) + " : " + GetString(d[key]) + ", ";
    }
    if (s != "{") s = s.Substring(0, s.Length - 2);
    return s + "}";
}

The Array, List and IEnumerable cases will all be covered by the IEnumerable overload. IEnumerable重载将涵盖Array,List和IEnumerable案例。 Note: Overload for String required as we don't want to treat it as IEnumberable. 注意:需要对String进行重载,因为我们不希望将其视为IEnumberable。

Just in case, if you are trying to generate json you can save yourself a lot of pain by just using a library like Json.NET , serializing objects is not as simple as you would think. 以防万一,如果你试图生成json,你可以通过使用像Json.NET这样的库来节省很多痛苦,序列化对象并不像你想象的那么简单。


You can create methods with the same name but different parameters and when you run the code C# will choose the right one to execute depending on the type you pass as an argument, you don't need to test the type of it. 您可以创建具有相同名称但不同参数的方法,并且当您运行代码时,C#将根据您作为参数传递的类型选择要执行的方法,您不需要测试它的类型。

You only need two overloads, one for IEnumerables which covers Lists and Arrays too, and one for Dictionary . 您只需要两个重载,一个用于IEnumerables ,它也包含ListsArrays ,另一个用于Dictionary

Also you can use some framework methods to join the strings string.Join which will add the separator only between elements, so you don't have to worry about removing trailing commas. 您还可以使用一些框架方法来连接字符串string.Join ,它将仅在元素之间添加分隔符,因此您不必担心删除尾随逗号。

Here's a small working example 这是一个小例子

using System;
using System.Collections.Generic;
using System.Linq;

namespace Experiments {
    class Program {
        static void Main(string[] args) {
            var array = new[] { "one", "two", "three" };
            var list = new List<string> { "four", "five", "six" };
            var dictionary = new Dictionary<string, string> {
                {"k1", "v1"},
                {"k2", "v2"},
                {"k3", "v2"},
            };

            Console.WriteLine(GetString(array));
            Console.WriteLine(GetString(list));
            Console.WriteLine(GetString(dictionary));

            Console.ReadLine();
        }

        public static string GetString<T>(IEnumerable<T> value) {
            return "["
                + string.Join(", ", value)
                + "]";
        }

        public static string GetString<TKey, TValue>(Dictionary<TKey, TValue> value) {
            return "{"
                + string.Join(", ", value.Select(entry => entry.Key + " : " + entry.Value))
                + "}";
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM