繁体   English   中英

如何将依赖项注入到 static class

[英]How to inject dependency to static class

在我的应用程序中,我经常想将日志消息写入磁盘。 我创建了一个简单的记录器 class 并使用依赖注入构建,如下所示:

var logger = new LogService(new FileLogger());
logger.WriteLine("message");

但这现在意味着我系统中需要记录的每个 class 都需要将这个LogService注入其中,这似乎是多余的。 因此,我想改为使用LogService static。 这样它就不需要注入到消费类中。

但问题来了。 如果我制作了一个记录器 class static,则无法通过构造函数将依赖项注入该 static ZA2F21A9F8EBC2CBCBDC4。

所以,我把我的LogService了这个。

public static class LogService()
{
    private static readonly ILoggable _logger;
    static LogService()
    {
         _logger = new FileLogger();
    }
    
    public static void WriteLine(string message) ...
}

这让我感觉很奇怪。 我认为这不再是 DI。

我将依赖项注入 static class 的最佳方法是什么?

作为一种实践,依赖注入旨在引入抽象(或接缝)来解耦易失性依赖。 易失性依赖是一个类或模块,除其他外,它可以包含非确定性行为,或者通常是您可以替换或拦截的东西。

有关 volatile 依赖项的更详细讨论,请参阅我的书的这个可自由阅读的介绍第 1.3.2 节

因为您的FileLogger写入磁盘,所以它包含非确定性行为 为此,您引入了ILoggable抽象。 这允许消费者与FileLogger实现分离。

为了能够成功地从解耦其挥发依赖消费,但是,你需要注入的依赖到消费者。 共有三种常见模式可供选择:

这两个构造器注入和属性注入在应用程序(又名的启动路径应用组成的根),并要求消费者存储以备后用一个私有字段的依赖。 这要求构造函数和属性是实例成员,即非静态的。 静态构造函数不能有任何参数,静态属性导致Ambient Context 反模式(参见第 5.3 节)和Temporal Coupling 这阻碍了可测试性和可维护性。

方法注入,另一方面,在施加的组合物根,它存储任何提供的相关性,而仅仅使用它。

因此,方法注入是三种模式中唯一可以同时应用于实例方法和静态方法的模式。

在这种情况下,方法的使用者必须提供依赖项。 然而,这确实意味着必须通过构造函数、属性或方法注入为使用者本身提供该依赖项。

您在其构造函数中创建FileLogger的静态LogService示例是紧密耦合代码的一个很好的示例。 这被称为 Control Freak 反模式(第 5.1 节),或者通常可以视为DIP 违规 DI相反

为了防止 volatile 依赖的紧耦合,最好的方法是使LogService非静态并将其 volatile 依赖注入到其唯一的公共构造函数中。

将依赖注入 (DI) 与静态类一起使用是没有意义的。 而不是 DI,只需向您的静态类添加一个初始化方法并传入依赖项。

public static class LogService
{
    private static ILoggable _logger;

    public static ILoggable Logger
    {
        get
        {
             return _logger;
        }
    }

    public static void InitLogger(ILoggable logger)
    {
         _logger = logger;
    }
}

要使用记录器,请确保首先调用InitLogger()

LogService.InitLogger(new FileLogger());
LogService.Logger.WriteLine("message");

您可以对需要注入到 static class 的任何 object 使用延迟初始化。

https://docs.microsoft.com/en-us/dotnet/api/system.lazy-1?view=net-5.0

这将允许您传递 static 对象,这些对象可以在运行的实例和需要使用这些对象的其他类/方法之间共享。 一个示例是您希望在整个应用程序中共享的 HttpClient。 您可以在 static class 中延迟初始化 HttpClient 并参考 static ZA2F2ED4F8EBC2CBB4C2 获取 HttpClient

这是使用 CosmosDB 客户端的另一个示例: https://docs.microsoft.com/en-us/azure/azure-functions/manage-connections?tabs=csharp#azure-cosmos-db-clients

以下是使用UnityLog4NetResolver扩展程序使用Unity DI容器注入log4net记录器的示例:

public class A
{
    readonly ILog log;

    public A(ILog log)
    {
        this.log = log;
    }

    ...
}

...

var container = new UnityContainer();

container
    .AddNewExtension<UnityLog4NetResolver.LogByTypeResolverExtension>();

container.Resolve<A>();

最后一个语句相当于new A(LogManager.GetLogger(typeof(A))) 因此,每个类可以具有由DI容器注入的具有相应名称的记录器。

这个想法来自这个方案 但是,它似乎被放弃了,并且不支持上一个Unity版本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM