简体   繁体   English

C# 检查类型是否实现了具有未知类型的特定泛型接口

[英]C# Check if type implements a specific generic interface with unknown types

I have multiple dependency injected handlers and want to find the appropriate one to solve a task.我有多个依赖注入处理程序,并希望找到合适的处理程序来解决任务。 I finally ended up finding the corresponding handler (in class HandlerResolver ) but i can't return the Handler as method result.我终于找到了相应的处理程序(在 class HandlerResolver ),但我无法将Handler程序作为方法结果返回。

Interfaces:接口:

public interface IMessage
{
}

public interface IHandler<TRoot, in TMessage> where TRoot : class
{
    public abstract TRoot Apply(TRoot root, TMessage message);
}

// Marker interface to register/resolve dependency injected handlers
public interface IHandler
{
}

Handlers:处理程序:

public class Handler1: IHandler<MyRoot, Message1>, IHandler
{
    public MyRoot Apply(MyRoot aggregateRoot, Message1 message)
    {
        ...
        return aggregateRoot;
    }
}


public class Handler2: IHandler<MyRoot, Message2>, IHandler
{
    public MyRoot Apply(MyRoot aggregateRoot, Message2 message)
    {
        ...
        return aggregateRoot;
    }
}

Messages:留言:

public class Message1 : ICaseMessage
{
    ...
}

public class Message2 : ICaseMessage
{
    ...
}

DI Registrations: DI注册:

services.AddScoped<IResolver, HandlerResolver>();
services.AddScoped<IHandler, Handler1>();
services.AddScoped<IHandler, Handler2>();

Resolve Handler:解决处理程序:

public class HandlerResolver : IResolver
{
    private IEnumerable<IHandler> Handlers { get; } // DI injected handlers
    public HandlerResolver(IEnumerable<IHandler> handlers)
    {
        Handlers = handlers;
    }


    public IHandler<TRoot, TMessage> GetHandler<TRoot, TMessage>(TRoot root, TMessage message)
        where TRoot : class
        where TMessage : class,
    {
        var concreteRootType = root.GetType();
        var concreteMessageType = message.GetType();

        var handlerOrNull = this.Handlers.FirstOrDefault(p =>
        {
            var genericArguments = p.GetType().GetInterfaces().First().GenericTypeArguments;
            return genericArguments[0] == concreteAggregateType && 
                   genericArguments[1] == concreteMessageType;

        });

        if (handlerOrNull == null)
        {
            throw new NotImplementedException($"No Handler Found");
        }
        else
        {
            return handlerOrNull as IHandler<TRoot, TMessage>;
        }
    }
}

return handlerOrNull as IHandler<TRoot, TMessage>;返回 handlerOrNull 作为 IHandler<TRoot, TMessage>;

This will always return null .这将始终返回null I think this is due to the parsing.我认为这是由于解析。 It seems trying to parse it into a IHandler<TRoot, IMessage> which for some reason doesn't work.似乎试图将其解析为IHandler<TRoot, IMessage>由于某种原因不起作用。

I have also tried this solution How to determine if a type implements a specific generic interface type which doesn't work if the generic type is not known.我也试过这个解决方案如何确定一个类型是否实现了一个特定的泛型接口类型,如果泛型类型未知,该接口类型就不起作用。

Typically you should register the actual full type of your objects, ie通常,您应该注册对象的实际完整类型,即

services.AddScoped<IHandler<MyRoot, Message1>, Handler1>();

that should let you just get the correct service for your types:这应该可以让您为您的类型获得正确的服务:

services.GetRequiredService<IHandler<MyRoot, Message1>>()

At least I think that is the correct methods to use for Microsoft.Extensions.DependencyInjection, it is not a library I'm familiar with.至少我认为这是用于 Microsoft.Extensions.DependencyInjection 的正确方法,它不是我熟悉的库。

Note that you need to register and resolve the actual types.请注意,您需要注册并解析实际类型。 Resolving IHandler<MyRoot, IMessage> will not work, since that would imply that the handlers could take any kind of IMessage -parameter, but they cannot, they can only take Message1 / Message2 .解析IHandler<MyRoot, IMessage>将不起作用,因为这意味着处理程序可以采用任何类型的IMessage参数,但它们不能,它们只能采用Message1 / Message2

Contravariance works in the opposite direction you seem to think, ie you would have a Handler1: IHandler<MyRoot, IMessage> that could be converted to a IHandler<MyRoot, Message1> .逆变的工作方向与您似乎认为的相反,即您将拥有一个Handler1: IHandler<MyRoot, IMessage>可以转换为IHandler<MyRoot, Message1> That works since the handler promises to accept all IMessage implementation, and Message1 is such an implementation.这是有效的,因为处理程序承诺接受所有IMessage实现,而Message1就是这样一个实现。 But it might cause trouble with resolving, since resolving typically require exact type matches.但它可能会给解析带来麻烦,因为解析通常需要精确的类型匹配。

You might get around that by resolving by hand, ie get the type object for your interface from the generic type arguments, and traverse the types by hand, checking if they are assignable.您可以通过手动解决来解决这个问题,即从通用类型 arguments 中为您的接口获取类型 object,然后手动遍历这些类型,检查它们是否可分配。 But this is not something I would recommend if you are not trying to do anything weird.但是,如果您不想做任何奇怪的事情,我不建议这样做。

    var handlerType = typeof(IHandler<TRoot, TMessage>);
    Handlers.FirstOrDefault(p => handlerType.IsAssignableFrom(p.GetType()));

The actual handler type should be IHandler<MyRoot, Message1> or IHandler<MyRoot, Message2> depending on the generic type parameters.实际的处理程序类型应该是IHandler<MyRoot, Message1>IHandler<MyRoot, Message2>取决于泛型类型参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM