Reputation: 1459
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
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