I\'m studying up on the unity containers and have a quick question on how to resolve a class\'s construction to multiple different implementations of an interface.
H
The problem is that you are resolving Canvas with the name "GL", but you have not registered Canvas in that way. Unity doesn't propagate the name to dependency resolution, so it won't use the name "GL" when resolving IRenderer.
There are several options to solve this already answered: Resolving named dependencies with Unity
Your question is whether this is a bad practice, or how you can achieve the same results. In my experience, trying to register and resolve multiple instances of the same interface usually leads to messy code. One alternative would be to use the Factory pattern to create instances of Canvas.
Do you need to use your container to resolve Canvas? If you don't have a reason not to, you could simply Resolve your IRenderer and new up a Canvas yourself:
new Canvas(container.Resolve<IRenderer>("GL"));
Remember that Unity is just a tool, if it doesn't seem to be capable of doing what you need, you may need a different kind of tool.
There is a way to inject the right renderer in the canvas on startup time. If you know the render method on startup you can register only the right renderer like this:
var container = new UnityContainer();
container.RegisterType<ICanvas, Canvas>();
if (CheckIfItIsDx11)
{
container.RegisterType<IRenderer, Dx11Renderer>();
}
else
{
container.RegisterType<IRenderer, GlRenderer>();
}
when you want to resolve the canvas just use:
var canvas = container.Resolve<ICanvas>();
if you dont know the renderer on startup time there is a way to. Like this:
container.RegisterType<IRenderer, Dx11Renderer>("DX11");
container.RegisterType<IRenderer, GlRenderer>("GL");
var renderer = container.Resolve<IRenderer>("DX11");
var canvas = container.Resolve<ICanvas>(new ParameterOverride("renderer", renderer));
Canvas now has the right renderer injected. The canvas can use the renderer interface like this:
internal interface ICanvas
{
void Draw();
}
public class Canvas : ICanvas
{
private readonly IRenderer _renderer;
private readonly List<Circle> _circles = new List<Circle>();
private readonly List<Square> _squares = new List<Square>();
public Canvas(IRenderer renderer)
{
_renderer = renderer;
}
public void Draw()
{
foreach (var circle in _circles)
{
_renderer.Draw(circle);
}
foreach (var square in _squares)
{
_renderer.Draw(square);
}
}
}
Also the renderer should not be drawing the shape. The shape is responsible for drawing itself. This way you keep your code at the same spot. If you keep adding shapes the renderer file get huge. and you need to search for some shapes if you want to change code. Now everything is in the right place where it should be. The code now should look something like this:
public interface IRenderer
{
void Draw(IShape shape);
}
public interface IShape
{
void Draw(IRenderer renderer);
}
public class Dx11Renderer : IRenderer
{
public void Draw(IShape shape)
{
shape.Draw(this);
}
}
public class GlRenderer : IRenderer
{
public void Draw(IShape shape)
{
shape.Draw(this);
}
}
public class Circle : IShape
{
public void Draw(IRenderer renderer)
{
if (renderer.GetType() == typeof(Dx11Renderer))
{
Console.WriteLine("Draw circle with DX11");
}
if (renderer.GetType() == typeof(GlRenderer))
{
Console.WriteLine("Draw circle with GL");
}
}
}
public class Square : IShape
{
public void Draw(IRenderer renderer)
{
if (renderer.GetType() == typeof(Dx11Renderer))
{
Console.WriteLine("Draw square with DX11");
}
if (renderer.GetType() == typeof(GlRenderer))
{
Console.WriteLine("Draw square with GL");
}
}
}
Hope this will help.