为什么我不能从具有泛型参数的类型转换为通用接口,该泛型参数是我的接口中泛型参数的子类型?

时间:2015-03-18 03:15:13

标签: c# generics

给出以下界面:

public interface IController<T> where T: Request {
    Response HandleRequest(T request);
}

以下请求类:

public BlergRequest : Request {
}

以下控制器实现:

public BlergController : IController<BlergRequest> {
    public Response HandleRequest(BlergRequest request) {
        return new Response("Here's your blergs!");
    }
}

为什么以下是InvalidCastException

IController<Request> controller = (IController<Request>)new BlergController();

在我的&#34;立即&#34;在调试器中的窗口,我有以下内容:

typeof(Request).IsAssignableFrom(typeof(BlergRequest))
true

typeof(IController<Request>).IsAssignableFrom(typeof(BlergController))
false

是什么给出的?我认为这是通用约束的全部要点?

2 个答案:

答案 0 :(得分:4)

从类型安全角度来看,您不允许这样做的事实是完全合乎逻辑的。如果IController<BlergRequest>在各种方法中接受BlergRequests(在您的示例中,HandleRequest就是这样一种方法),那么这些方法可能会调用仅的成员strong> BlergRequest上可用,而不是Request。因此,您无法将IController<BlergRequest>分配(或转发)给IController<Request>

另一方面,如果您的界面仅返回 BlergRequest,并且从未使用过它们,那么它可能会将BlergRequest返回给需要{的任何消费代码{1}}。在这种情况下,您的界面在Request中将是协变,您可以使用协变T修饰符标记类型参数T。然后,您可以在需要out的任何地方指定IController<BlergRequest>

答案 1 :(得分:2)

跟进@Asad的评论;试试这个:

public class Request { }
public class Response {
  public Response(string text) {}
}

public interface IController<out T> where T: Request {
    Response HandleRequest(T request);
}

public class BlergRequest : Request { }

public class BlergController : IController<BlergRequest> {
    public Response HandleRequest(BlergRequest request) {
        return new Response("Here's your blergs!");
    }

    public void Test() {
      IController<Request> controller = new BlergController();
    }
}

但是,IController.HandleRequest的参数(可能)不是写成的协变。然而,扩展接口的使用,使得编译干净,没有变化的规范,这可能满足您的需求:

public interface IRequest { }

public class Request : IRequest { }
public class Response {
  public Response(string text) {}
}

public interface IController<T> where T: IRequest {
    Response HandleRequest(T request);
}

public class BlergRequest : IRequest { }

public class BlergController : IController<IRequest> {
    public Response HandleRequest(BlergRequest request) {
        return new Response("Here's your blergs!");
    }

    public void Test() {
      IController<IRequest> controller = new BlergController();
    }
}