Home > Software engineering >  Create instance of implementation of generic class based on type of Class<T>
Create instance of implementation of generic class based on type of Class<T>

Time:11-29

I have the following class layout:

abstract class GenericClass<TArgs> where TArgs : ArgsBaseClass { }
abstract class ArgsBaseClass { }

class RandomArgs : ArgsBaseClass { }
class RandomClass : GenericClass<RandomArgs> { }

Now I want to be able to create an instance of RandomClass from an instance of RandomArgs, without knowing that it should be RandomClass. I.e. somehow I need to derive the fact that RandomClass implements GenericClass<RandomArgs>.

I'm able to create an instance of GenericClass<RandomArgs> using:

void CreateSpecificInstance(ArgsBaseClass args)
{
    Type genericType = typeof(GenericClass<>).MakeGenericType(typeof(args));
    var genericInstance = Activator.CreateInstance(genericType, typeof(args));
    // But then I need help for the following step:
    Type specificType = ...  // in this case RandomClass, but should be derived from 'args'.
    var specificInstance = (specificType)genericInstance;
}

Any help is appreciated.


EDIT:

Basically I have multiple args classes. Each args class has a respective 'normal' class that can be initialised with this args class. Hence GenericClass<ArgsBaseClass>.

Now I want to be able to create an instance of the 'normal' class whenever I pass an instance of the ArgsBaseClass to some function (in my example named CreateSpecificInstance.

As per my comments, I guess it would be possible to create a mapping between each args class and 'normal' class e.g. using a dictionary, but whenever you add a new args 'normal' class combination you also need to update this dictionary. Hence I guess a solution using reflection should be used here, however I don't see a solution using reflection that doesn't iterate over all classes in the assembly or namespace, hence I'm asking for help.

CodePudding user response:

The most important thing to answer this question is a lot of really specific details, particular to your implementation.

So, this answer is not a specific solution, but aims to describe a set of solutions, or at least ingredients for the solution.

The most difficult thing you can encounter in this kind of problems is dynamic discovery of interfaces and types. E.g. you don't know at compile time the full list of Args types and GenericClass implementations.

If you know it beforehand, you're lucky. Just load a Dictionary<Type, Type> with the Args type as a key and the implementation type as value.

If you don't, you have an extra problem to solve: how to uniquely identify the implementation types? There are many way to solve this problem, too, but they involve some form of Assembly.GetTypes() or (better yet) using a good IoC framework (i like Autofac the most) with built in assembly scanning capabilities. You would need to solve also the problem (maybe just in principle, but it's a question you need an answer for) of what happens if you find two implementations for the same args.

Back to our implementation type. We have an Args instance or type, we have our dictionary of types, so we find the Type of the correct implementation of GenericClass<TArgs>. Now you need a way to build instances. If you can ensure you have a parameterless constructor, you can just call Activator.CreateInstance(myGenericType). This will give you an instance of RandomClass, boxed inside an object.

You will need to cast it in some way, in order to use it, maybe to GenericClass<RandomArgs>.

If you do not have a parameterless constructor, and you can, try using an IoC container to make the hard work of discovering the constructor depencencies for you. Again, Autofac is a nice candidate, but there are a lot of alternatives and this is a deep rabbit hole.

So, in easiest case, you just need something like this:


private Dictionary<Type, Type> _implementations =
    new Dictionary<Type, Type>();

public void RegisterImplementation<TArgs, TImpl>()
    where TArgs : ArgsBaseClass
    where TImpl : GenericClass<TArgs>, new()
{
    _implementations[typeof(TArgs)] = typeof(TImpl);
}

public GenericClass<TArgs> MakeClass<TArgs>(TArgs args) where TArgs : ArgsBaseClass
{
    var implementationType = _implementations.TryGetValue(typeof(TArgs), out var t)
        ? t
        : throw new ArgumentException("The args type "   typeof(TArgs)   " is unrecognized.", nameof(args));

    return (GenericClass<TArgs>)Activator.CreateInstance(implementationType);
}

CodePudding user response:

I have decided to go another route:

I have updated my ArgsBaseClass to the following:

abstract class ArgsBaseClass
{
    abstract Type InstanceType { get; }
}

now whenever I create a new implementation of this class I'm forced to do something like:

class RandomArgs : ArgsBaseClass 
{
    override Type InstanceType => typeof(RandomClass);
}

Then my CreateSpecificInstance becomes:

void CreateSpecificInstance(ArgsbaseClass args)
{
    Type specificType = args.InstanceType;
    var specificInstance = (specificType)Activator.CreateInstance(specificType, args);
}
  • Related