Reputation: 15226
Given the following interface:
public interface IController<T> where T: Request {
Response HandleRequest(T request);
}
And the following Request class:
public BlergRequest : Request {
}
And the following controller implementation:
public BlergController : IController<BlergRequest> {
public Response HandleRequest(BlergRequest request) {
return new Response("Here's your blergs!");
}
}
Why is the following an InvalidCastException
?
IController<Request> controller = (IController<Request>)new BlergController();
In my "Immediate" window in the debugger, I have the following:
typeof(Request).IsAssignableFrom(typeof(BlergRequest))
true
typeof(IController<Request>).IsAssignableFrom(typeof(BlergController))
false
What gives? I thought this was the whole point of generic constraints?
Upvotes: 1
Views: 286
Reputation: 11893
To follow up on @Asad's comment; try this:
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();
}
}
However, the argument to IController.HandleRequest is (probably) not covariant as written. Extending the use of interfaces however, makes this compile cleanly without a specification of variance, which may meet your needs:
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();
}
}
Upvotes: 2
Reputation:
The fact that you're not allowed to do this is perfectly logical from a type safety perspective. If an IController<BlergRequest>
accepts BlergRequests
in various methods (in your example, HandleRequest
is one such method), it stands to reason that those methods might invoke members that are only available on a BlergRequest
, and not on a Request
. Thus you can't assign (or cast) an IController<BlergRequest>
to an IController<Request>
.
If on the other hand your interface was only returning BlergRequest
s, and never consumed them, then it could return a BlergRequest
to any consuming code that required a Request
. In such a case, your interface would be covariant in T
, and you could mark the type parameter T
with the covariant out
modifier. You could then assign IController<BlergRequest>
wherever an IController<Request>
is needed.
Upvotes: 5