Search code examples
c#typestype-inference

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.


Solution

  • 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<T> cases will all be covered by the IEnumerable overload. Note: overload for string required as we don't want to treat it as IEnumerable<Char>.