【问题标题】:Unity Container Multiple Implementations of same interfaceUnity Container 同一接口的多个实现
【发布时间】:2016-12-15 17:52:48
【问题描述】:

我正在研究统一容器,并有一个关于如何将类的构造解析为接口的多个不同实现的问题。

这是我的代码:

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

这会引发“ResolutionFailedException”,所以我一定是错误地使用了它。

有人可以解释这是否是不好的做法,或者我如何实现这一点。

谢谢

更新:

所以我所做的是使用每种类型的依赖项注册 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");

这对我很有效!

【问题讨论】:

标签: c# .net unity-container


【解决方案1】:

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

有几个选项可以解决这个问题:Resolving named dependencies with Unity

您的问题是这是否是一种不好的做法,或者您如何才能获得相同的结果。根据我的经验,尝试注册和解析同一接口的多个实例通常会导致代码混乱。一种替代方法是使用工厂模式来创建 Canvas 的实例。

您需要使用您的容器来解析 Canvas 吗?如果您没有理由不这样做,您可以简单地解决您的 IRenderer 并自己新建一个 Canvas:

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

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

【讨论】:

  • 谢谢,我可能对 Unity 容器的真正用途感到困惑。
  • 我已经用适合我的解决方案更新了我的问题。
【解决方案2】:

有一种方法可以在启动时在画布中注入正确的渲染器。如果你知道启动时的渲染方法,你可以像这样注册正确的渲染器:

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

希望这会有所帮助。

【讨论】:

  • "渲染器也不应该绘制形状。" - 如果您采用数据驱动的方法,那么这不一定是正确的。渲染器绝对应该在绘制形状。
  • 我不熟悉数据驱动范式。我将对此进行调查。感谢您的反馈:)
  • 我相信它的基本要点是您将数据与行为分开。那么像 Circle 这样的对象就变成了一个纯数据类(可能有半径、颜色、碰撞数据等)。然后你有一个(或多个)类负责对数据执行操作(绘制、碰撞等)。看看 google 中的实体和组件系统。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-06-10
  • 1970-01-01
  • 2014-01-24
  • 2020-12-25
  • 1970-01-01
  • 1970-01-01
  • 2018-05-10
相关资源
最近更新 更多