如何从类型实例解析封闭通用类型的实例并使用它?

问题描述 投票:0回答:2

假设您有几个开放的通用抽象类:

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

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

然后您有几个实现:

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...
    }
}

现在您要在程序集中的IoC容器中注册所有实现,以便可以解决依赖关系:

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

现在您有一个ImplementationTypes的列表,您想遍历该列表并将每个类型的实例解析为其封闭的通用类型。这是发生问题的地方:

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

所以第一个问题是:如何从类型实例解析封闭的通用类型的实例?

[我曾考虑创建一个可以用Activator.CreateInstance()实例化的通用包装器类,然后让包装器解析正确的处理程序,但不确定是否这是正确的方法。

此外,可以说您要具有不同通用参数类型的处理程序列表:

// 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>();

并且您想通过它们的类型参数以某种方式连接它们:

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

我如何将IActionHandler或IFunctionHandler的实例下放到其封闭的通用接口?

此问题的解决方案可能与第一个问题的解决方案相同。我只将它们分开,因为解决第一个问题的方法可能是错误的解决方法。

任何类型的输入将不胜感激!如果有更好的方法,我也很高兴听到这一消息。

谢谢!

c# generics reflection dependency-injection autofac
2个回答
0
投票

这种形式的未构造泛型类型(typeof(AbstractActionHandler<>)typeof(AbstractFunctionHandler<,>))被称为泛型类型定义。您可以使用反射来确定类型在其基类之一中是否具有特定的泛型类型定义。例如,

    private static bool HasGenericBase(Type type, Type baseGenericTypeDefinition)
    {
        if (!baseGenericTypeDefinition.IsGenericTypeDefinition)
            throw new ArgumentException($"The specified type ({baseGenericTypeDefinition}) is not a generic type definition");
        while (type.BaseType != typeof(object))
        {
            type = type.BaseType;
            if (type.IsGenericTypeDefinition && type == baseGenericTypeDefinition)
            {
                return true;
            }
            if (type.IsGenericType && type.GetGenericTypeDefinition() == baseGenericTypeDefinition)
            {
                return true;
            }
        }
        return false;
    }

您可以像这样在模型中使用它:

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

0
投票

我认为您可能要避免存储抽象类型的实现列表(即容器的用途)。

我正在想象您的程序中的某个时刻,您想访问AbstractActionHandler<TInput>的实现,无论可能是什么,然后使用它。

如果您向Autofac索取AbstractiActionHandler<int>AbstractActionHandler<string>,它将找出要使用的操作实现中的哪一个,并将其返回给您。

我这里有一个例子,解释了我的意思:

static async Task Main(string[] args)
{
    var builder = new ContainerBuilder();
    var assembly = Assembly.GetExecutingAssembly();

    builder.RegisterAssemblyTypes(assembly)
        .AsClosedTypesOf(typeof(AbstractActionHandler<>));

    builder.RegisterAssemblyTypes(assembly)
        .AsClosedTypesOf(typeof(AbstractFunctionHandler<,>));

    var container = builder.Build();

    // At some point later...
    await InvokeActionHandler(container, 100);

    // or...
    await InvokeActionHandler(container, "something");
}

private static Task InvokeActionHandler<TInput>(IContainer container, TInput inputArg)
{
    var handler = container.Resolve<AbstractActionHandler<TInput>>();

    return handler.Action(inputArg);
}

您可以使用相同的方法获取AbstractionFunctionHandler<TInput, TOutput>

关于如何将它们链接在一起,如果我们不想过多更改您的基类,这会变得有些复杂;一个选项可能是在Metadata中附加函数注册的输出类型,并声明一个仅泛型输入到函数输入的接口。

然后,您可以在运行时确定所需的操作类型(例如,包装类型)并对其进行解析。

类似这样的东西:

class Program
{

    static async Task Main(string[] args)
    {
        var builder = new ContainerBuilder();
        var assembly = Assembly.GetExecutingAssembly();

        builder.RegisterAssemblyTypes(assembly)
            .AsClosedTypesOf(typeof(AbstractActionHandler<>));

        builder.RegisterAssemblyTypes(assembly)
            .AsClosedTypesOf(typeof(AbstractFunctionHandler<,>))
            // Registering a second service here.
            .AsClosedTypesOf(typeof(IDynamicFunctionHandler<>))
            // Capturing the output type argument (this is not robust if there are multiple inheritance levels).
            .WithMetadata("output", t => t.BaseType.GenericTypeArguments[1]);

        // Register an open-generic registration wrapper.
        builder.RegisterGeneric(typeof(FuncActionCombo<>));

        var container = builder.Build();

        await InvokeActionFromFunctionResult(container, 100);
    }

    private static async Task InvokeActionFromFunctionResult<TInput>(IContainer container, TInput input)
    {
        // Resolve our combo class (that is only typed to the input).
        var funcHandler = container.Resolve<FuncActionCombo<TInput>>();

        await funcHandler.Invoke(input);
    }
}

public interface IDynamicFunctionHandler<TInput>
{
    Task<object> FunctionDynamic(TInput input);
}

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

    // Just wrap the regular function.
    public async Task<object> FunctionDynamic(TInput input) => await Function(input);
}

public class FuncActionCombo<TInput>
{
    private readonly IDynamicFunctionHandler<TInput> _funcHandler;
    private readonly object _actionHandler;
    private readonly MethodInfo _actionInvokeMethod;

    public FuncActionCombo(IComponentContext context, Meta<IDynamicFunctionHandler<TInput>> handlerWithMeta)
    {
        _funcHandler = handlerWithMeta.Value;

        // Get the output type data we attached to the registration.
        var outputType = (Type) handlerWithMeta.Metadata["output"];

        // Create a closed type of the action handler from the output type.
        var closedActionHandler = typeof(AbstractActionHandler<>).MakeGenericType(outputType);

        // Resolve the implementation.
        _actionHandler = context.Resolve(closedActionHandler);

        // Get the method declaration.
        _actionInvokeMethod = closedActionHandler.GetMethod(nameof(AbstractActionHandler<object>.Action));
    }

    public async Task Invoke(TInput input)
    {
        var result = await _funcHandler.FunctionDynamic(input);

        // Invoke our dynamic Action.
        await (Task) _actionInvokeMethod.Invoke(_actionHandler, new[] { result });
    }
}

仍然不是很好,但是其他任何事情都可能需要从根本上重新思考您如何实现这一目标。

© www.soinside.com 2019 - 2024. All rights reserved.