user3401335
user3401335

Reputation: 2405

Cast class with covariance in generic

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

Answers (1)

Guru Stron
Guru Stron

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

Related Questions