Resolución dinámica de tipos en .NET Core
Tags: csharp, dependencyinversion, ioc, keyedservices
Published: 2023-10-21 08:22:24



![Resolución dinámica de tipos en tiempo de ejecución en el contenedor de IoC de .NET Core](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rzdizz3citonlhcgx276.jpg)

En ocasiones necesitamos resolver, en tiempo de ejecución, diferentes implementaciones de una misma interfaz.

Por ejemplo, podemos contar con una abstracción declarada a través de la interfaz _IShape_ que es implementada por las clases _Rectangle_ y _Circle_. Para ello podemos registrar los dos tipos en el contenedor de inyección de dependencias ([IoC](https://martinfowler.com/bliki/InversionOfControl.html)) -en adelante contenedor- y esperar que dicho contenedor nos devuelva el tipo adecuado cuando lo necesitemos.

```cs
services.AddTransient<IShape, Rectangle>();
services.AddTransient<IShape, Circle>();
```
Sin embargo, cuando el contenedor se encuentre con una dependencia de _IShape_ no sabrá qué tipo debe resolver, si _Rectangle_ o _Circle_.

```cs
private readonly IShape _rectangle;
private readonly IShape _circle;

public WindowManager(IShape rectangle, IShape circle)
{
    this._rectangle = rectangle ?? throw new ArgumentNullException(nameof(rectangle));
    this._circle = cirlce ?? throw new ArgumentNullException(nameof(circle));
}
```
Al resolver las dependencias de _WindowManager_, el contenedor nos devolverá siempre el mismo tipo.

Algunos autores proponen soluciones que utilizan factorías para la resolución de los tipos, sin embargo, dicha solución conlleva la necesidad de modificar la factoría cada vez que necesitamos registrar un nuevo tipo, y dependiendo de cómo construyamos la factoría corremos el riego de romper el principio [Open-Closed](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle) además de añadir complejidad a nuestra solución.

> Al utilizar factorías podríamos romper el principio [Open-Closed](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle) y añadir complejidad nuestro código.

Como anotación, cabe mencionar que previo a las versiones .NET Core, era habitual utilizar el contenedor de [Ioc Unity](http://unitycontainer.org/tutorials/registration/metadata.html) de Microsoft, en el que se podían registrar tipos “nombrados” mediante un string para poder hacer referencia al tipo concreto cuando se necesitase:

```cs
//Registro de tipos nombrados en el IoC Unity
container.RegisterType<IShape, Rectangle>("Rectangle");
container.RegisterType<IShape, Circle>("Circle");
```
Sin embargo en el contenedor incluido por defecto en _.NET Core_ no contamos con semejante posibilidad.

Para solventar esta situación, necesitamos un mecanismo que indique al contenedor cómo resolver el tipo adecuado en las dependencias que lo necesiten. Una forma sencilla pasa por utilizar una interfaz genérica.

```cs
public interface IShape<T> where T : class {}
```
Las diferentes clases, en nuestro ejemplo _Rectangle_ y _Circle_, implementarán ahora dicha interfaz genérica:

```cs
public class Rectangle : IShape<Rectangle>
{
    public Rectangle() { }
}
```
Como podemos ver la interfaz especifica aquí el mismo tipo que la utiliza; En este caso la clase _Rectangle_. Este es el truco que utilizaremos para registrar los tipos en el contenedor. De esta manera podemos indicar el tipo tanto en el registro como en la dependencia.

```cs
services.AddTransient<IShape<Rectangle>, Rectangle>();
services.AddTransient<IShape<Circle>, Circle>();
```
Ahora ya podemos declarar las dependencias utilizando la interfaz genérica y el contenedor resolverá el tipo adecuado.

```cs
private readonly IShape<Rectangle> _rectangle;
private readonly IShape<Circle> _circle;

public WindowManager(IShape<Rectangle> rectangle, IShape<Circle> circle)
{
    this._rectangle = rectangle ?? throw new ArgumentNullException(nameof(rectangle));
    this._circle = circle ?? throw new ArgumentNullException(nameof(circle));
}
```

## Conclusión
Como decíamos existen otras posibles formas de solucionar este problema, sin embargo, se suele hacer uso de factorías que añaden complejidad y corren el riesgo de romper el principio [Open-Closed](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle). El uso de interfaces genéricas en este caso es posiblemente la solución más sencilla.

## Código de ejemplo
En el siguiente enlace se puede descargar el código de ejemplo descrito en el artículo: [Código ejemplo](https://github.com/jsanjuan2016/DynamicDI-simple).

#### Referencias
Artículo originalmente publicado en [Medium](https://medium.com/@jsanjuanart/resoluci%C3%B3n-din%C3%A1mica-de-tipos-en-tiempo-de-ejecuci%C3%B3n-en-el-ioc-de-net-core-c9b43e240dd0)