Jeff Dege
Jeff Dege

Reputation: 11680

How to cast an object to a generic return type, when the return type is a generic interface?

I have an astoundingly ugly method that's supposed to pull assorted data from a database, and use it construct an object of a requested type. The goal is to pull the assorted ugliness into a single place, so that classes that derive from the class containing this method can be relatively clean.

I'm not having a problem with when it's supposed to return a single object, even when the type is nullable:

protected T getValue<T>(ParameterObject myParameterObject)
{
    var typeOfT = typeof (T);

    if (typeOfT == typeof(bool))
    {
        string val = this.getValue(myParameterObject);

        if (val == null)
            return default(T);

        bool rVal;
        if (!Boolean.TryParse(val, out rVal))
            return default(T);

        return changeType<T>(rVal);
    }

    if (typeOfT == typeof (double?))
    {
        string val = this.getValue(myParameterObject);

        if (val == null)
            return default(T);

        double rVal;
        if (!Double.TryParse(val, out rVal))
            return default(T);

        return changeType<T>(rVal);
    }

    [...]
}

public static T changeType<T>(object value)
{
    var t = typeof(T);
    if (!t.IsGenericType || t.GetGenericTypeDefinition() != typeof(Nullable<>))
        return (T)Convert.ChangeType(value, t);

    if (value == null)
        return default(T);

    t = Nullable.GetUnderlyingType(t);
    return (T)Convert.ChangeType(value, t);
}

Unfortunately, I have cases in which my desired return type is IList, where I want to return a List:

protected T getValue<T>(ParameterObject myParameterObject)
{
    var typeOfT = typeof (T);

    if (typeOfT.IsGenericType)
    {
        if (typeOfT.GetGenericTypeDefinition() == typeof (IList<>))
        {
            var genericArgumentType = typeOfT.GenericTypeArguments.FirstOrDefault();
            var baseType = typeof (List<>);
            var genericType = baseType.MakeGenericType(new[] {genericArgumentType});

            var rVal = Activator.CreateInstance(genericType);

            [code to populate the list] 

            return changeType<T>(rVal);
        }

    [...]
}

I can't return an rVal, because it's an object, not an instance of T. But my changeType() function is failing, with an error:

System.InvalidCastException: Object must implement IConvertible.

So, I have a generic method for which the return type is IList, and I have an object which is a List, but my reference to it is of type Object. How do I cast it so that I can return it from the function?

Upvotes: 6

Views: 1291

Answers (1)

Frank Bryce
Frank Bryce

Reputation: 8446

Your return (T)Convert.ChangeType(value, t); line is failing because your value object does not implement IConvertible.

From MSDN docs

[...] For the conversion to succeed, value must implement the IConvertible interface, because the method simply wraps a call to an appropriate IConvertible method. The method requires that conversion of value to conversionType be supported.

To fix this, I'm not sure of a general way but for the specific case of value being of an IEnumerable or IEnumerable<> type, then you could try to cast then perform a value.Select(x => changeType<ListGenT>(x)) linq query. By using your changeType method recursively this should handle nested generic types as well.

Upvotes: 1

Related Questions