我对泛型和 C# 中的不变性和协变性有疑问
我想要cast handlerConcrete,这里是代码
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var handlerConcrete = new HandlerTestConcrete<MessageBaseTestConcrete, ResponseMessageBaseTestConcrete>() { };
var cast = handlerConcrete as IHandlerTest<MessageBaseTest, ResponseMessageBaseTest>;
}
}
public interface IHandlerTest<in TRequest, out TResponse>
where TRequest : MessageBaseTest
where TResponse : ResponseMessageBaseTest
{
GenericResponse<TResponse> Handle(TRequest request);
}
public class HandlerTestConcrete<TRequest, TResponse> : IHandlerTest<TRequest, TResponse>
where TRequest : MessageBaseTest
where TResponse : ResponseMessageBaseTest
{
public Task<GenericResponse<TResponse>> Handle(TRequest request)
{
throw new System.NotImplementedException();
}
}
public class ResponseMessageBaseTest { }
public class MessageBaseTest { }
public class ResponseMessageBaseTestConcrete: ResponseMessageBaseTest { }
public class MessageBaseTestConcrete: MessageBaseTest { }
public interface GenericResponse<TResponse> where TResponse : ResponseMessageBaseTest
{
TResponse Response { get; }
}
错误是
无效方差:类型参数 TResponse 在 IHandlerTest
.Handle(TRequest) 上必须始终有效。 TResponse 是协变的
我的目的是用这行代码进行转换
var cast = handlerConcrete as IHandlerTest<MessageBaseTest, ResponseMessageBaseTest>;
由于多种原因这是不可能的。首先,类不支持 C# 中的方差,因此
Task
和 GenericResponse
都不能用作 Handle
的返回参数。后者可以通过引入一个界面来解决 - public interface IGenericResponse<out TResponse> where TResponse : ResponseMessageBaseTest
,但对于 Task
来说,事情就没那么容易了。
但主要缺陷是
new HandlerTestConcrete<MessageBaseTestConcrete, ...>
不是 IHandlerTest<MessageBaseTest, ...>
,原因非常明显 - 它要求“至少”传入 MessageBaseTestConcrete
,因此传递 MessageBaseTest
打破了这一要求。但使用 MessageBaseTestConcrete
应该可以:
var cast = handlerConcrete as IHandlerTest<MessageBaseTestConcrete, ResponseMessageBaseTest>;
或任何继承人(
MessageBaseTestConcreteConcrete : MessageBaseTestConcrete
):
var cast = handlerConcrete as IHandlerTest<MessageBaseTestConcreteConcrete, ResponseMessageBaseTest>;