簡體   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