Unity Container多个相同接口的实现

时间:2014-05-15 04:52:46

标签: c# .net unity-container

我正在研究统一容器,并快速询问如何将一个类的构造解决为多个不同的接口实现。

这是我的代码:

public interface IRenderer
{
    void DrawSquare(Square square);
    void DrawCircle(Circle circle);
}

public interface IShape
{
    void Draw(IRenderer renderer);
}

public class Dx11Renderer : IRenderer
{
    public void DrawSquare(Square square)
    {
    }

    public void DrawCircle(Circle circle)
    {
    }
}

public class GlRenderer : IRenderer
{
    public void DrawSquare(Square square)
    {
    }

    public void DrawCircle(Circle circle)
    {
    }
}

public class Circle : IShape
{
    public void Draw(IRenderer renderer) { renderer.DrawCircle(this); }   
}

public class Square
{
    public void Draw(IRenderer renderer) { renderer.DrawSquare(this); }   
}

public class Canvas
{
    private readonly IRenderer _renderer;

    private List<Circle> _circles = new List<Circle>();
    private List<Square> _squares = new List<Square>(); 

    public Canvas(IRenderer renderer)
    {
        _renderer = renderer;
    }

    public void Draw()
    {
        foreach (Circle c in _circles)
        {
            c.Draw(_renderer);
        }

        foreach (Square s in _squares)
        {
            s.Draw(_renderer);
        }
    }
}

并注册/解决

        // Create the container
        var container = new UnityContainer();

        // registration

        container.RegisterType<IRenderer, GlRenderer>("GL");
        container.RegisterType<IRenderer, Dx11Renderer>("DX11");

        Canvas canvas = container.Resolve<Canvas>("GL");

这会抛出&#34; ResolutionFailedException&#34;所以我必须错误地使用它。

有人可以解释这是不好的做法,或者我是如何做到的。

由于

更新:

所以我所做的就是在每种类型的依赖项中注册Canvas两次,如下所示:

// Canvas with an OpenGL Renderer
container.RegisterType<Canvas>("GLCanvas", new InjectionConstructor(new ResolvedParameter<IRenderer>("GL")));
// Canvas with a DirectX Renderer
container.RegisterType<Canvas>("DXCanvas", new InjectionConstructor(new ResolvedParameter<IRenderer>("DX11")));

Canvas canvas = container.Resolve<Canvas>("GLCanvas");

这对我有用!

2 个答案:

答案 0 :(得分:6)

问题是您正在使用名称“GL”解析Canvas,但是您没有以这种方式注册Canvas。 Unity不会将名称传播到依赖项解析,因此在解析IRenderer时不会使用名称“GL”。

有几种方法可以解决这个问题:Resolving named dependencies with Unity

您的问题是这是一种不好的做法,还是您如何取得相同的结果。根据我的经验,尝试注册和解析同一界面的多个实例通常会导致代码混乱。另一种方法是使用Factory模式创建Canvas实例。

您是否需要使用容器来解析Canvas?如果你没有理由,你可以简单地解决你的IRenderer并自己新建一个画布:

new Canvas(container.Resolve<IRenderer>("GL"));

请记住,Unity只是一种工具,如果它似乎无法满足您的需求,您可能需要一种不同的工具。

答案 1 :(得分:1)

有一种方法可以在启动时在画布中注入正确的渲染器。如果您在启动时知道渲染方法,则只能注册正确的渲染器:

var container = new UnityContainer();
container.RegisterType<ICanvas, Canvas>();
if (CheckIfItIsDx11)
{
    container.RegisterType<IRenderer, Dx11Renderer>();
}
else
{
    container.RegisterType<IRenderer, GlRenderer>();
}

当您想要解析画布时,只需使用:

var canvas = container.Resolve<ICanvas>();

如果您在启动时不知道渲染器,则可以使用。像这样:

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现在注入了正确的渲染器。画布可以使用渲染器界面,如下所示:

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);
        }
    }
}

渲染器也不应该绘制形状。形状负责绘制自己。这样您就可以将代码保存在同一位置。如果继续添加形状,渲染器文件会变得很大。如果你想改变代码,你需要搜索一些形状。现在一切都在正确的地方。代码现在应该是这样的:

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");
        }
    }
}

希望这会有所帮助。