为什么一个类派生自一个带有where子句的抽象类转换为它的最低公共类

Rob*_*ert 6 c# inheritance abstract-class

一些代码来复制问题:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

据我所知,LoginRequest类是一个Request <Response>,因为它继承自Request <T>,而LoginResponse继承自Response,所以任何人都可以告诉我为什么我得到编译器错误?

注意:我也试过一个明确的演员

Jus*_*ner 8

您收到错误是因为Request<Response>并且Request<LoginResponse>不是协变的.

仅仅因为LoginResponse继承Response并不意味着Request<LoginResponse>可以像对待一样Request<Response>.阅读本文:

MSDN - 泛型中的协方差和逆变


Ric*_*lly 7

因为您的泛型参数是隐式不变的 - 两种类型,Request<LoginResponse>并且Request<Response>完全不同.C#4.0引入了委托类型和接口的差异,可以在这里为您提供解决方案:

public interface IResponse<out T> where T : Response {}
Run Code Online (Sandbox Code Playgroud)

这里我们将泛型类型声明TCovariant.

Eric Lippert撰写了许多关于C#差异主题的好博客文章,我强烈建议阅读它们.


Chr*_*ain 7

这是因为C#泛型类不是协变的.C#试图阻止你做以下事情:

Request<Response> castTest = login;
castTest.Response = someOtherKindOfResponse;
Run Code Online (Sandbox Code Playgroud)

使用列表可能更清楚这个例子.想象一下,如果以下工作:

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
Run Code Online (Sandbox Code Playgroud)