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);
}