我在玩cqs,我想在类库中实现它(所以没有IOC,IServiceProvider等)。这是我写的一些代码:

public interface IQuery<TResult>
{
}

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    TResult Handle(TQuery query);
}

public class Query : IQuery<bool>
{
    public int Value { get; set; }
}

public class QueryHandler : IQueryHandler<Query, bool>
{
    public bool Handle(Query query)
    {
        return query.Value > 0;
    }
}

public class Dispatcher
{
    private readonly Dictionary<Type, object> handlers = new Dictionary<Type, object>();

    public Dispatcher()
    {
        handlers.Add(typeof(Query), new QueryHandler());
    }

    public T Dispatch<T>(IQuery<T> query)
    {
        IQueryHandler<IQuery<T>, T> queryHandler;

        if (!this.handlers.TryGetValue(query.GetType(), out object handler) ||
            ((queryHandler = handler as IQueryHandler<IQuery<T>, T>) == null))
        {
            throw new Exception();
        }

        return queryHandler.Handle(query);
    }
}

这就是我如何调用我的代码:
Query query = new Query();
Dispatcher dispatcher = new Dispatcher();
var result = dispatcher.Dispatch(query);

但是问题在于,在调度程序内部,我不知道为什么不能将变量处理程序转换为IQueryHandler<IQuery<T>,T>。这里是一些额外的数据:
c# - 与C#中的泛型类型有些混淆-LMLPHP

PS:我知道如何使此功能(动态)生效,但是我想了解为什么此代码无法正常工作。

最佳答案

该代码不起作用,因为IQueryHandler通用参数上的TQuery是不变的。 TQuery必须是协变的,以便handler可转换为IQueryHandler<IQuery<T>, T>,但这是不可能的,我将在后面解释。但是,您可以使TQuery互变,这使您可以将handler转换为IQueryHandler<ASubclassOfQuery, T>TResult可以是协变的。这是执行此操作的代码:

public interface IQueryHandler<in TQuery, out TResult> where TQuery : IQuery<TResult>

有关泛型方差的更多信息,请参见this page

至于为什么handler不是IQueryHandler<IQuery<T>, T>,我们首先假设它是,这意味着该代码可以编译:
IQueryHandler<IQuery<T>, T> q = handler;
q.Handle(new MyQuery<T>());
MyQuery的定义如下:
class MyQuery<T> : IQuery<T> {}

但是,handler的运行时类型为QueryHandlerQueryHandler.Handle仅处理Query对象,而不处理MyQuery<T>对象!我们有一个矛盾,因此我们对handlerIQueryHandler<IQuery<T>, T>的假设必须为假。

关于c# - 与C#中的泛型类型有些混淆,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57918641/

10-11 01:14