Llevo utilizando Castle Windsor como contenedor de inversión de control durante bastante tiempo, pero hasta hace poco no me había fijado en la forma en que resuelve tipos genéricos.
Cuando registramos un tipo genérico abierto, Castle es capaz de resolverlo usando los parámetros de tipo que sea necesario. Por ejemplo, si tenemos un repositorio genérico de esos que no me gustan, podemos hacer algo así:
public class Customer {} public class Product {} public interface IRepository<T> {} public class Repository<T> {} [TestFixture] public class ResolveTest { [Test] public void Resolve_Generic_Open_Type() { var container = new WindsorContainer(); container.Register(Component.For(typeof(IRepository<>)) .ImplementedBy(typeof(Repository<>)); Assert.That(container.Resolve<IRepository<Product>>(), Is.TypeOf<Repository<Product>>)); } }
Esto es algo bastante normal en cualquier contenedor de inversión de control. Lo que no sabía es que si se ha registrado una implementación cerrada del interface genérico, tiene prioridad sobre la implementación abierta.
Siguiendo con el ejemplo anterior, si tuviéramos una implementación especial para IRepository<Customer>
, quedaría:
public class Customer {} public class Product {} public interface IRepository<T> {} public class Repository<T> {} public class CustomerRepository : IRepository<Customer> {} [TestFixture] public class ResolveTest { [Test] public void Resolve_Generic_Closed_Type() { var container = new WindsorContainer(); container.Register(Component.For(typeof(IRepository<>)) .ImplementedBy(typeof(Repository<>)); container.Register(Component.For(typeof(IRepository<Customer>)) .ImplementedBy(typeof(CustomerRepository)); Assert.That(container.Resolve<IRepository<Product>>(), Is.TypeOf<Repository<Product>>)); Assert.That(container.Resolve<IRepository<Customer>>(), Is.TypeOf<CustomerRepository>)); } }
En este caso, Castle podría resolver el primer componente registrado (que además es el componente por defecto, debido al ordern de registro), pero es lo bastante listo como para darse cuenta de que hay un componente «mejor» registrado y usarlo.
Esta técnica nos permite configurar de forma fácil implementaciones genéricas de componentes y especilizarlas sólo para aquellos componentes que sea necesario.