Vic F
Vic F

Reputation: 1459

Set type dynamically for method call

I have a static function:

    public static MyCollection<T> Parse<T>(string? json) where T : BaseClass
    {
       ...
    }

And it works fine. It makes decisions internally based on the type and produces the correct collection.

But, the collection I want is based on a file name coming in from another source and I was hoping to have one collection and dynamically set the type, as in:

   Type t;
   if (name == "Certain name") t = typeof(CertainChildClassOfBaseClass);
   if (name == "Other name") t = typeof(OtherChildClassOfBaseClass);

   var myCollection = ParsingService.Parse<t>(jsonString);

This obviously doesn't work, but is what I'm trying possible? I don't really want to write a bunch of if/else blocks with nearly identical content just so each one can have a different type (I already wrote those type-specific blocks inside the Parse function - doing the same branching twice seems like maybe I've made a more fundamental mistake). I am open to suggestions on how to do this the "right" way. Maybe I have to do this with reflection, which doesn't thrill me, but isn't the end of the world. One possible issue is the my parsing service is static.

Help is always appreciated.

Upvotes: 0

Views: 593

Answers (1)

Etienne de Martel
Etienne de Martel

Reputation: 36852

Usually, I would recommend making Parse non generic, and then have a generic overload:

public static MyCollection<BaseClass> Parse(Type type, string? json)
{
   ...
}

public static MyCollection<T> Parse<T>(string? json) where T : BaseClass
{
   return (MyCollection<T>)Parse(typeof(T), json);
}

But you can't make MyCollection contravariant because it's a concrete class, so that cast is illegal. And anyway, contravariant containers are dangerous to use (just look at arrays, for example).

The other method is to use reflection to get the version of the method you want (a process known as "closing" the generic type):

var genericMethod = typeof(/* class containing Parse */).GetMethod("Parse");
var method = genericMethod.MakeGenericMethod(type); // type will be substituted for your 'T', above
object collection = method.Invoke(json); // call the method

And here we hit another snag: collection's type will be object, and you need to cast it to something else. But you can't use MyCollection<T> because you don't know T, and you can't use MyCollection<BaseClass> because, well, no contravariance, again.

So really the main blocker is that MyCollection type. As long as it is that way, you have no easy way to go around it. You could instead return a non generic collection, like ICollection or IList, but then you lose type safety and you'll have to keep casting more later. It's up to you, really.

Upvotes: 2

Related Questions