简体   繁体   中英

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. But if not, then it's builtin .ToString() method is called.

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;

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.


As per David Manning's example, I've modified the code to look as such;

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. Passing a List object to it returns the string 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. 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. Note: Overload for String required as we don't want to treat it as 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.


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.

You only need two overloads, one for IEnumerables which covers Lists and Arrays too, and one for 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.

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))
                + "}";
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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