在WPF開發中,依賴註入(Dependency Injection)和控制反轉(Inversion of Control)是程式解耦的關鍵,在當今軟體工程中占有舉足輕重的地位,兩者之間有著密不可分的聯繫。今天就以一個簡單的小例子,簡述如何在WPF中實現依賴註入和控制反轉,僅供學習分享使用,如有不足之... ...
在WPF開發中,依賴註入(Dependency Injection)和控制反轉(Inversion of Control)是程式解耦的關鍵,在當今軟體工程中占有舉足輕重的地位,兩者之間有著密不可分的聯繫。今天就以一個簡單的小例子,簡述如何在WPF中實現依賴註入和控制反轉,僅供學習分享使用,如有不足之處,還請指正。
什麼是依賴註入和控制反轉?
依賴註入又稱為依賴項註入,那什麼是依賴項呢?比如在一個類A中,實現某中功能,而此功能是另外一個類B實現的,那就說明A依賴B,B就是A的依賴項。或者是另一個對象A所依賴的對象B。示例如下:
namespace DemoIoc
{
public class MessageWriter
{
public void Print(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}
public class Worker : BackgroundService
{
private readonly MessageWriter writer = new();
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
writer.Print($"Worker running at: {DateTimeOffset.Now}");
await Task.Delay(1_000, stoppingToken);
}
}
}
}
註意:在上述示例中,Worker類依賴於MessageWriter類,所以MessageWriter就是Worker的依賴項。 硬編碼的依賴項(如前面的示例)會產生問題,應避免使用。
強依賴關係具有以下幾個問題:
- 如果要用不同的實現替換
MessageWriter
,必須修改Worker
類。 - 如果
MessageWriter
具有依賴項,則必須由Worker
類對其進行配置,且很難進行初始化。 - 這種實現很難進行單元測試。
那如何解決上述依賴關係所造成的弊端呢?答案就是依賴項註入。可通過如下幾個步驟實現:
- 使用介面或基類將依賴關係實現抽象化。
- 在服務容器中註冊依賴關係。
- 將服務註入到使用它的類的構造函數中。
.NET 提供了一個內置的服務容器 IServiceProvider。 服務通常在應用啟動時註冊,並追加到 IServiceCollection。 添加所有服務後,可以使用 BuildServiceProvider 創建服務容器。 框架負責創建依賴關係的實例,併在不再需要時將其釋放。
簡單一句話說:依賴註入(DI)將所依賴的對象參數化,介面化,並且將依賴對象的創建和釋放剝離出來,這樣就做到瞭解耦,並且實現了控制反轉(IoC)。
控制反轉(IoC)具有如下兩個特點:
- 高等級的代碼不能依賴低等級的代碼;
- 抽象介面不能依賴具體實現;
控制反轉解決代碼的強耦合,增加了代碼的可擴展性。依賴註入將依賴具體實現類和控制實現類的創建和釋放,變成了依賴介面或抽象類,不再控制介面的創建和釋放。兩者之間相輔相成,互相成就。
WPF中實現依賴註入的步驟
1. 安裝DI庫
首先創建一個WPF應用程式,然後在Nuget包管理器中安裝微軟提供的依賴註入庫【Microsoft.Extensions.DependencyInjection】,如下所示:
2. 創建介面和實現類
創建測試用的介面ITextService和實現類TextService,如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DemoIoc
{
public interface ITextService
{
public string GetText();
}
public class TextService : ITextService
{
public string GetText()
{
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
}
}
}
3. 介面註入
在需要調用的地方(如:MainWindow)進行ITextService介面註入,如下所示:
namespace DemoIoc
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ITextService textService;
public MainWindow(ITextService textService)
{
this.textService = textService;
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.txtCurrentTime.Text = textService.GetText();
}
}
}
註意:以上可以看出MainWindow依賴ITextService介面,而不依賴於介面的實現。這樣就實現了依賴註入。
4. 配置容器
在啟動程式App.xaml.cs中,添加當前對象成員,和服務提供對象,併在實例化服務對象的時候一次性註冊,以便在後續需要的時候進行獲取。如下所示:
namespace DemoIoc
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
/// <summary>
/// 獲取當前 App 實例
/// </summary>
public new static App Current => (App)Application.Current;
/// <summary>
/// 獲取存放應用服務的容器
/// </summary>
public IServiceProvider ServiceProvider { get; }
public App()
{
ServiceProvider = ConfigureServices();
}
/// <summary>
/// 配置應用的服務
/// </summary>
private static IServiceProvider ConfigureServices()
{
var serviceCollection = new ServiceCollection()
.AddSingleton<ITextService,TextService>()
.AddSingleton<MainWindow>();
return serviceCollection.BuildServiceProvider();
}
protected override void OnStartup(StartupEventArgs e)
{
var mainWindow = ServiceProvider.GetRequiredService<MainWindow>();
mainWindow.Show();
}
}
}
註意:在此示例中,MainWindow通過服務註冊的方式進行實例化,所以需要刪除預設的App.xaml中StartUri屬性設置,否則將提示預設構造函數不存在。
示例測試
經過上述步驟,就實現了WPF中依賴註入和控制反轉,測試結果如下:
說明:正常輸出,則表示依賴註入成功。
參考文檔
1. .Net依賴項註入:https://learn.microsoft.com/zh-cn/dotnet/core/extensions/dependency-injection
以上就是依賴註入和控制反轉的全部內容,希望可以拋磚引玉,一起學習,共同進步。
作者:小六公子
出處:http://www.cnblogs.com/hsiang/
本文版權歸作者和博客園共有,寫文不易,支持原創,歡迎轉載【點贊】,轉載請保留此段聲明,且在文章頁面明顯位置給出原文連接,謝謝。
關註個人公眾號,定時同步更新技術及職場文章