为什么不能从具有泛型参数的类型强制转换为泛型接口,泛型参数是接口中泛型参数的子类型



给定以下接口:

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();

在调试器的"立即"窗口中,我有以下内容:

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

什么东西?我以为这就是通用约束的全部意义?

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

另一方面,如果您的接口仅返回BlergRequest s,并且从未使用过它们,那么它可以向任何需要Request的使用代码返回BlergRequest。在这种情况下,您的接口将是T中的协变,并且您可以用协变out修饰符标记类型参数T。然后,您可以在需要IController<Request>的任何位置分配IController<BlergRequest>

跟进@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();
    }
}

最新更新