C# MediatR泛型类型约束报错问题求助
问题:MediatR泛型类型约束导致的转换错误
问题背景
我正在开发一个使用MediatR包的C#应用程序,作为C#新手,搞不懂自己编写的泛型类型约束存在什么问题。空类/接口即可复现问题,相关代码如下:
MediatR核心代码
public interface IRequest<out TResponse> { ... } public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { ... }
我的实现代码
// 所有响应DTO的通用接口 public interface IResponseDto { ... } // 项目仓库接口 public interface IItemRepository { ... } // 响应描述类 public class Response<TResponseDto> where TResponseDto : class, IResponseDto { ... } // 请求基类,要求派生类实现MediatR的IRequest接口 public abstract class BaseRequest<TResponseDto> : IRequest<Response<TResponseDto>> where TResponseDto : class, IResponseDto { ... } // 请求处理基类,要求派生类实现MediatR的IRequestHandler接口 public abstract class BaseRequestHandler<TRepository, TRequest> : IRequestHandler<TRequest, Response<IResponseDto>> where TRequest : class, IRequest<Response<IResponseDto>> { ... }
使用时的错误
当我尝试按如下方式使用上述类时出现错误:
public class ReadItemResponseDto : IResponseDto { ... } public class ReadItemQuery : BaseRequest<ReadItemResponseDto> { ... } // 此处报错 public class ReadItemQueryHanlder : BaseRequestHandler<IItemRepository, ReadItemQuery> { ... }
错误信息:无法将参数ReadItemQuery用作TRequest类型的参数,因为无法从ReadItemQuery转换为MediatR.IRequest<Response<IResponseDto>>
我无法理解错误原因——ReadItemQuery继承自BaseRequest
错误原因
问题出在泛型协变性的限制上:
IRequest<out TResponse>虽然是协变接口(带有out关键字),但Response<TResponseDto>是一个不变的类(C#中类不支持协变/逆变,只有接口和委托可以)。Response<ReadItemResponseDto>无法隐式转换为Response<IResponseDto>,因此ReadItemQuery实现的IRequest<Response<ReadItemResponseDto>>,无法被视为IRequest<Response<IResponseDto>>,不符合BaseRequestHandler的泛型约束要求。
解决方案
修改BaseRequestHandler的泛型定义,让它和请求的响应DTO类型联动,避免直接约束到IRequest<Response<IResponseDto>>:
方案1:增加响应DTO泛型参数
// 修改请求处理基类 public abstract class BaseRequestHandler<TRepository, TRequest, TResponseDto> : IRequestHandler<TRequest, Response<TResponseDto>> where TResponseDto : class, IResponseDto where TRequest : class, IRequest<Response<TResponseDto>> where TRepository : class, IItemRepository { ... }
使用时传入对应的响应DTO类型:
public class ReadItemQueryHanlder : BaseRequestHandler<IItemRepository, ReadItemQuery, ReadItemResponseDto> { ... }
方案2:直接约束到BaseRequest子类
如果想更明确关联请求基类,可以将TRequest的约束改为BaseRequest<TResponseDto>:
public abstract class BaseRequestHandler<TRepository, TRequest, TResponseDto> : IRequestHandler<TRequest, Response<TResponseDto>> where TResponseDto : class, IResponseDto where TRequest : BaseRequest<TResponseDto> where TRepository : class, IItemRepository { ... }
使用方式和方案1一致,这样能确保TRequest是你的自定义请求基类的派生类,避免无关类型进入约束。
内容的提问来源于stack exchange,提问作者Gianluca Locri
相关产品推荐
相关产品推荐

