jeudi 11 juin 2020

How to resolve an instance of a closed generic type from a type instance and use it?

Lets say you have a couple of open generic abstract classes:

public abstract class AbstractActionHandler<TInput>
{
    public abstract Task Action(TInput input);
}

public abstract class AbstractFunctionHandler<TInput, TOutput>
{
    public abstract Task<TOutput> Function(TInput input);
}

Then you have a couple of implementations:

public class IntActionClass : AbstractActionHandler<int>
{
    public Task Action(int input)
    {
        //do some work...
    }
}
public class StringActionClass : AbstractActionHandler<string>
{
    public Task Action(string input)
    {
        //do some work...
    }
}
public class IntFunctionClass : AbstractFunctionHandler<int, string>
{
    public Task<string> Function(int input)
    {
        //do some work...
    }
}
public class StringFunctionClass : AbstractFunctionHandler<string, bool>
{
    public Task<bool> Function(string input)
    {
        //do some work...
    }
}

Now you want to register all of the implementations in your assembly in your IoC container so that dependencies can be resolved:

var builder = new ContainerBuilder();
var openGenericTypes = new[]
{
    typeof(AbstractActionHandler<>),
    typeof(AbstractFunctionHandler<,>)
};

var assembly = Assembly.GetExecutingAssembly();
var implementationTypes = new List<Type>();

foreach (var openGenericType in openGenericTypes)
{
    var types = assembly.GetTypes()
        .Where(type => type.IsAssignableFrom(openGenericType))
        .Where(type => !type.IsAbstract);

    builder.RegisterTypes(types.ToArray())
        .AsClosedTypesOf(openGenericType)
        .InstancePerDependency();

    implementationTypes.AddRange(types);
}

var container = builder.Build();

Now you have a list of implementationTypes, and you want to iterate through the list and resolve an instance of each type as their closed generic type. This is where the problem occurs:

foreach (var type in implementationTypes)
{
    var handler = container.Resolve(type); 
    // handler is of type object but needs to be of its closed generic type
    // ie. AbstractAbstractHandler<int>

    if (type == typeof(AbstractActionHandler<>))
    {
        // This should be called for all classes closing AbstractActionHandler<>
        ConstructActionHandler<TInput>(handler); // calls AbstractActionHandler.Action
    }
    else if (type == typeof(AbstractFunctionHandler<,>))
    {
        // This should be called for all classes closing AbstractFunctionHandler<>
        ConstructFunctionHandler<TInput, TOutput>(handler); // calls AbstractFunctionHandler.Function
    }
}

public void ConstructActionHandler<TInput>(AbstractActionHandler<TInput> handler)
{
    var actionHandlerWrapperDelegate = new Func<TInput, Task>(async (input) =>
    {
        await actionHandler.Action(input);
    });
    // ....
}

So the first question is: how do i resolve an instance of a closed generic type from a type instance?

I thought about creating a generic wrapper class that can be instantiated with Activator.CreateInstance() and let the wrapper resolve the correct handler, but im not sure if that would be the correct approach.

Furthermore, lets say you want to have a list of handlers of different generic argument types:

// this would not be possible
var actionHandlers = new List<AbstractActionHandler<TInput>>();

// this would however be possible
var actionHandlers = new List<IActionHandler>();
var functionHandlers = new List<IFunctionHandler>();

And you want to connect them in some way by their type argument:

foreach (var actionHandler in actionHandlers)
{
    var actionHandlerTypeArgument = handler.GetType().GenericTypeArguments.First();
    var functionHandler = functionHandlers.Find(handler => 
            handler.GetType().GenericTypeArguments.First() == actionHandlerTypeArgument);

    var wrapper = new Func<TInput>(async input => 
    {
        var output = functionHandler.Function(input);
        actionHandler.Action(output);
    });
    // ...
}

How would i downcast an instance of IActionHandler or IFunctionHandler to its closed generic interface?

The solution to this question might be the same as the solution to the first question. I only separated them since the approach to the first problem might be the wrong way to go about it.

Any type of input would be highly appreciated! If there is a better approach to this, i'd be glad to hear that too.

Thanks!





Aucun commentaire:

Enregistrer un commentaire