Given these interface and implementation
interface IService {}
class Service : IService {}
with a generic method
void Register<I>(I service)
{
var type = typeof(I);
}
How to make the following lines be consistent regarding the generic type?
Register<IService>(new Service()) // type is 'IService'
Register(new Service()); // type is 'Service'
Two options are acceptable:
- both lines are compiled using
IServietype - second line fails to compile
As you note,
Register(new Service());of course compiles toRegister<Service>(new Service());Assuming that you can define some logic that would pick one of the concrete type’s implemented interfaces as the interface to register (which is a big assumption), you could handle concrete types rather than having the compiler exclude them. Obviously the trivial solution is to require that the type implement only one interface, but that’s unlikely to be very useful.
I’m thinking of something along these lines (taking Jon Skeet’s suggestion and renaming the type parameter):
All things considered, it’s probably simplest and clearest to let the caller specify the desired interface with the
Register<IService>(new Service());call.EDIT
To answer that question, let’s consider the semantics of the call. The call is associating an object (
new Service()) with an interface (IService). One way to force programmers to be explicit about the identity of the interface is to make the type a formal parameter. In fact, if you’re not calling any of the interface’s methods on the object you’re registering, you don’t even need generics:Even without calling any of the interface’s members on the object, however, there’s another benefit from generics: compile-time type checking. Is there a way to get compile-time type checking while forcing the developer to specify the type explicitly? Yes: with two type parameters in the method signature, but only one argument, there’s no way for compiler to infer both types, so the developer has to supply both. That’s more typing, but the code is more explicit.
With that approach, you can also constrain the implementing type to the interface type, to ensure that the developer doesn’t call, for example,
Register<IValidationService, SecurityService>(new SecurityService());:or even
This gets us back to the point of some possibly-redundant type indications, and the call is even more verbose than what you started with, but it does prevent the developer from inadvertently registering the wrong service type.
We are still left with the original problem, however, as there’s nothing stopping the developer from calling
That won’t fail until a runtime check for
typeof(TServiceType).IsInterface.