Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 396
Jeff Dege Avatar asked Dec 30 '15 22:12

Jeff Dege


People also ask

Can you cast to a generic type Java?

The Java compiler won't let you cast a generic type across its type parameters because the target type, in general, is neither a subtype nor a supertype.

Can Java return type be generic?

So methods of generic or nongeneric classes can use generic types as argument and return types as well. Here are examples of those usages: // Not generic methods class GenericClass < T > { // method using generic class parameter type public void T cache ( T entry ) { ... } }

Can you define a generic method in a non-generic class in Java?

Yes, you can define a generic method in a non-generic class in Java.


1 Answers

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.

like image 124
Frank Bryce Avatar answered Nov 02 '22 09:11

Frank Bryce