Why is a class derived from an abstract class with a where clause different from its lowest common class

Some code to replicate the problem:

using System; public abstract class Response { } public abstract class Request<T> where T : Response { } public class LoginResponse : Response { } public class LoginRequest : Request<LoginResponse> { } public class Program { static void Main(string[] args) { LoginRequest login = new LoginRequest(); /* Error: Cannot implicitly convert type 'LoginRequest' to 'Request' */ Request<Response> castTest = login; /* No Error */ Request<LoginResponse> castTest2 = login; } } 

As far as I can tell, the LoginRequest class is Request <Response> because it inherits from Request <T> and LoginResponse inherits from Response, so can someone tell me why I get a compiler error?

note: I also tried explicit casting

+6
source share
4 answers

You get an error because Request<Response> and Request<LoginResponse> are not covariant.

Just because LoginResponse inherits from Response does not mean that Request<LoginResponse> can be treated in the same way as Request<Response> . Read this article:

MSDN - covariance and contravariance in general

+8
source

Since your general parameter is implicitly invariant, the two types of Request<LoginResponse> and Request<Response> completely different. C # 4.0 introduced variance in delegate types and interfaces and can provide you with a solution:

 public interface IResponse<out T> where T : Response {} 

Here we declared the generic type T as Covariant .

Eric Lippert wrote a lot of good blog posts on the subject of variance in C #, I would highly recommend reading them.

+7
source

This is because C # common classes are not covariant. C # is trying to keep you from doing the following:

 Request<Response> castTest = login; castTest.Response = someOtherKindOfResponse; 

This example is perhaps clearer in the lists. Imagine if the following worked:

 var someStringList = new List<String>(); var someObjectList = ((List<Object>)someStringList; // This throws a compile exception, thankfully someObjectList.Add(1); // If the above worked, then this would compile, but would throw a runtime exception 
+7
source

LoginRequest does not follow from Request<Response> , it comes from Request<LoginResponse> .

A generic type is the compiled type itself. The template parameter hierarchy does not matter.

+3
source

Source: https://habr.com/ru/post/906680/


All Articles