You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#泛型匹配抽象类类型失败及协变逆变简化方案咨询

问题分析与解决方案

原代码失败原因

你的第一段代码中,if (handler is Handler<IRequest<T>, T> h)的类型检查始终失败,核心原因是:

  • 泛型类不支持协变或逆变,GetAgeHandler是Handler<GetAge, int>的实例,而Handler<GetAge, int>和Handler<IRequest<int>, int>是完全独立的封闭构造类型,无法互相转换。
  • 类型检查不通过后直接返回default,导致预期的20无法输出。

利用协变/逆变简化实现

不需要额外添加多层接口,只需定义一个支持逆变的泛型接口即可解决问题。泛型接口可以通过in和out关键字实现类型协变/逆变,而类不支持这一特性。

简化后的代码

void Main()
{
    var requestHandler = new RequestHandler();
    var result = requestHandler.Handle(new GetAge());
    Console.WriteLine(result); // 输出20
}

public interface IRequest<T> { }

public class GetAge : IRequest<int> { }

// 定义支持逆变的请求处理器接口:
// - in TRequest:逆变参数,允许用子类请求类型的处理器处理父类请求类型
// - out TResponse:协变参数,允许返回子类响应类型的处理器适配父类响应类型
public interface IHandler<in TRequest, out TResponse> where TRequest : IRequest<TResponse>
{
    TResponse Handle(TRequest request);
}

// 抽象基类封装类型转换逻辑,子类只需实现具体业务
public abstract class HandlerBase<TRequest, TResponse> : IHandler<TRequest, TResponse> 
    where TRequest : IRequest<TResponse>
{
    public TResponse Handle(TRequest request)
    {
        return HandleRequest(request);
    }

    protected abstract TResponse HandleRequest(TRequest request);
}

public class GetAgeHandler : HandlerBase<GetAge, int>
{
    protected override int HandleRequest(GetAge request)
    {
        return 20;
    }
}

public class RequestHandler
{
    private readonly Dictionary<Type, object> _handlers = new()
    {
        [typeof(GetAge)] = new GetAgeHandler()
    };

    public TResponse Handle<TResponse>(IRequest<TResponse> request)
    {
        var requestType = request.GetType();
        if (_handlers.TryGetValue(requestType, out var handler))
        {
            // 利用逆变特性,将HandlerBase<GetAge, int>转换为IHandler<IRequest<TResponse>, TResponse>
            if (handler is IHandler<IRequest<TResponse>, TResponse> typedHandler)
            {
                return typedHandler.Handle((IRequest<TResponse>)request);
            }
        }
        return default;
    }
}

核心原理

  • 逆变参数in TRequest:当TRequest标记为in时,IHandler<GetAge, int>可以安全地转换为IHandler<IRequest<int>, int>,因为GetAge是IRequest<int>的子类,处理器能够接受更通用的IRequest<int>类型作为输入。
  • 这个特性绕过了泛型类的类型限制,让你无需额外定义IHandler<TResponse>这类中间接口,就能完成类型匹配与调用。

扩展支持多请求类型

如果需要添加GetName这类其他请求,只需新增请求类、处理器类,并在字典中注册即可,逻辑完全复用:

public class GetName : IRequest<string> { }

public class GetNameHandler : HandlerBase<GetName, string>
{
    protected override string HandleRequest(GetName request)
    {
        return "Foo";
    }
}

// 在RequestHandler的_handlers中添加:
[typeof(GetName)] = new GetNameHandler()

内容的提问来源于stack exchange,提问作者Marat K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 05:45:13