Почему класс не наследуется от абстрактного класса с предложением where, приведенным к его низшему общему классу - PullRequest
6 голосов
/ 23 января 2012

Некоторый код для повторения проблемы:

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;
    }
}

Насколько я могу сказать, класс LoginRequest является запросом , потому что он наследуется от запроса , а LoginResponse наследуется от ответа, поэтому кто-нибудь может объяснить мне, почему я получаю ошибку компилятора?

примечание: я также пробовал явное приведение

Ответы [ 4 ]

8 голосов
/ 23 января 2012

Вы получаете ошибку, потому что Request<Response> и Request<LoginResponse> не являются ковариантными.

То, что LoginResponse наследуется от Response, не означает, что к Request<LoginResponse> можно относиться так же, как к Request<Response>. Дайте эту статью читать:

MSDN - Ковариация и контравариантность в родовых типах

7 голосов
/ 23 января 2012

Это потому, что универсальные классы C # не являются ковариантными. C # пытается удержать вас от следующих действий:

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

Этот пример, возможно, более понятен в списках. Представьте, что сработало следующее:

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 голосов
/ 23 января 2012

Поскольку ваш универсальный параметр неявно инвариантен - два типа Request<LoginResponse> и Request<Response> полностью различны.C # 4.0 ввел дисперсию в типах делегатов и интерфейсах и может предоставить вам решение здесь:

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

Здесь мы объявили универсальный тип T как Ковариант .

Эрик Липперт написал много хороших постов в блоге на тему дисперсии в C #, я очень рекомендую прочитать их.

3 голосов
/ 23 января 2012

LoginRequest не происходит от Request<Response>, оно происходит от Request<LoginResponse>.

Универсальный тип - это сам по себе тип после компиляции.Шаблонная иерархия параметров не имеет значения.

...