如何使用要解析的类型来解析依赖项

本文关键字:依赖 类型 何使用 | 更新日期: 2023-09-27 18:33:49

我有几个类采用 ILogger 类型的依赖项。ILogger的实现需要知道它是记录器的类型,即FooILogger将被new Logger(typeof(Foo)),对于Bar它将new Logger(typeof(Bar)),等等。

我希望 Unity 自动注入适当的记录器;换句话说,当我调用 container.Resolve<Foo>() 时,我希望将new Logger(typeof(Foo))注入到Foo实例中。

如何在 Unity 中进行此设置?有没有办法将要解析的类型传递给依赖项?

(在我的实际代码中,我实际上有一个带有Create方法的ILoggerFactory,该方法也将类型作为参数。所以我可以将工厂传递给我的班级,他们会自己打电话给Create以获得合适的记录器,但它并不像我想要实现的那么优雅(


一些代码使事情更清晰:

interface ILogger
{
    ...
}
class Logger : ILogger
{
    private readonly Type _type;
    public Logger(Type type)
    {
        _type = type;
    }
    ...
}
class Foo
{
    private readonly ILogger _logger;
    public Foo(ILogger logger) // here I want a Logger with its type set to Foo
    {
        _logger = logger;
    }
}

这个相关的问题准确地显示了我想要做什么,而接受的答案正是我正在寻找的那种东西......但它是针对 NInject 的,而不是 Unity。

如何使用要解析的类型来解析依赖项

下面是

一个容器扩展,它将记录器构造函数的 Type 参数设置为注入 ILogger 的类型。

瞬态 IBuilderContext.Policy 用于存储 ILogger 注入到的类型。

也许它比需要的更复杂,但这似乎有效

public class LoggerExtension : UnityContainerExtension
{
    public static NamedTypeBuildKey LoggerBuildKey = new NamedTypeBuildKey<Logger>();
    protected override void Initialize()
    {
        Context.Strategies.Add(new LoggerTrackingPolicy(), UnityBuildStage.TypeMapping);
        Context.Strategies.Add(new LoggerBuildUpStrategy(), UnityBuildStage.PreCreation);
    }
}
public class LoggerTrackingPolicy : BuilderStrategy
{
    public LoggerTrackingPolicy()
    {
    }
    public override void PreBuildUp(IBuilderContext context)
    {
        if (context.BuildKey.Type != typeof(Logger))
        {
            var loggerPolicy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey);
            if (loggerPolicy == null)
            {
                loggerPolicy = new LoggerPolicy();
                context.Policies.Set<ILoggerPolicy>(loggerPolicy, LoggerExtension.LoggerBuildKey);
            }
            loggerPolicy.Push(context.BuildKey.Type);
        }
    }
}
public class LoggerBuildUpStrategy : BuilderStrategy
{
    public LoggerBuildUpStrategy()
    {
    }
    public override void PreBuildUp(IBuilderContext context)
    {
        if (context.BuildKey.Type == typeof(Logger))
        {
            var policy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey);
            Type type = policy.Peek();
            if (type != null)
            {
                context.AddResolverOverrides(new ParameterOverride("type", new InjectionParameter(typeof(Type), type)));
            }
        }
    }
    public override void PostBuildUp(IBuilderContext context)
    {
        if (context.BuildKey.Type != typeof(Logger))
        {
            var policy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey);
            policy.Pop();
        }
    }
}
public interface ILoggerPolicy : IBuilderPolicy
{
    void Push(Type type);
    Type Pop();
    Type Peek();
}
public class LoggerPolicy : ILoggerPolicy
{
    private Stack<Type> types = new Stack<Type>();
    public void Push(Type type)
    {
        types.Push(type);
    }
    public Type Peek()
    {
        if (types.Count > 0)
        {
            return types.Peek();
        }
        return null;
    }
    public Type Pop()
    {
        if (types.Count > 0)
        {
            return types.Pop();
        }
        return null;
    }
}

它的工作原理是:当一个不是记录器的类型试图被解析时,在TypeMapping阶段(在任何创建之前(,该类型被推送到堆栈上。 稍后,在创建之前,如果类型是 Logger,则会从堆栈中探出它所注入的类型,并将该类型用作解析程序重写。 创建后,如果类型不是记录器,则会从堆栈中弹出。

还有一些代码来确保它正常工作(我在记录器中添加了一个 Type 属性只是为了验证它是否正确设置(:

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();
        container.RegisterType<ILogger, Logger>();
        container.AddNewExtension<LoggerExtension>();
        var a = container.Resolve<A>();
        var b = container.Resolve<B>();
        var c = container.Resolve<C>();
        var d = container.Resolve<D>();
        var x = container.Resolve<X>();
    }
}
public interface ILogger
{
    Type Type { get; }
}
public class Logger : ILogger
{
    private readonly Type _type;
    public Logger(Type type)
    {
        _type = type;
    }
    public Type Type { get { return _type; } }
}
public class A
{
    public A(ILogger logger)
    {
        System.Diagnostics.Debug.Assert(logger.Type == typeof(A));
    }
}
public class B
{
    public B(ILogger logger)
    {
        System.Diagnostics.Debug.Assert(logger.Type == typeof(B));
    }
}
public class C
{
    public C(A a, D d, B b, ILogger logger)
    {
        System.Diagnostics.Debug.Assert(logger.Type == typeof(C));
    }
}
public class D
{
    public D()
    {
    }
}
public class X
{
    public X(Y y)
    {
    }
}
public class Y
{
    public Y(ILogger logger)
    {
        System.Diagnostics.Debug.Assert(logger.Type == typeof(Y));
    }
}

正如@spotco所建议的,我也推荐泛型。 统一注册将使用"开放泛型"完成,如下所示。 通过此单一注册,您可以解析 Logger 的泛型参数中的任何类型。

unityContainer.RegisterType(typeof(ILogger<>), typeof(Logger<>));

使用该注册,可以使用如下所示的构造函数注入解析实例。

public class MyClass 
{
    public MyClass(ILogger<MyClass> logger) { ... }
}

更多信息在这里。