Reputation: 2405
I have a problem with invariance and covariance in generic and c#
I want cast handlerConcrete, here the code
[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; }
}
The error is
Invalid variance: The type parameter TResponse must be invariantly valid on IHandlerTest<TRequest, TResponse>.Handle(TRequest). TResponse is covaiant
my purpose is to cast with this line of code
var cast = handlerConcrete as IHandlerTest<MessageBaseTest, ResponseMessageBaseTest>;
Upvotes: 1
Views: 51
Reputation: 142253
That is not possible for multiple reasons. First of all variance in C# is not supported for classes, hence neither Task
nor GenericResponse
can be used as return parameter for Handle
. Latter could be workaround by introducing an interface - public interface IGenericResponse<out TResponse> where TResponse : ResponseMessageBaseTest
but for Task
it would not be so easy.
But the main flaw is that new HandlerTestConcrete<MessageBaseTestConcrete, ...>
is not IHandlerTest<MessageBaseTest, ...>
for quite obvious reasons - it requires "at least" MessageBaseTestConcrete
to be passed in, so passing MessageBaseTest
breaks this requirement. But using MessageBaseTestConcrete
should work:
var cast = handlerConcrete as IHandlerTest<MessageBaseTestConcrete, ResponseMessageBaseTest>;
Or any inheritor (MessageBaseTestConcreteConcrete : MessageBaseTestConcrete
):
var cast = handlerConcrete as IHandlerTest<MessageBaseTestConcreteConcrete, ResponseMessageBaseTest>;
Upvotes: 2