深入理解WPF中的依赖注入和控制反转

简介: 在WPF开发中,依赖注入(Dependency Injection)和控制反转(Inversion of Control)是程序解耦的关键,在当今软件工程中占有举足轻重的地位,两者之间有着密不可分的联系。今天就以一个简单的小例子,简述如何在WPF中实现依赖注入和控制反转,仅供学习分享使用,如有不足之处,还请指正。

在WPF开发中,依赖注入(Dependency Injection)和控制反转(Inversion of Control)是程序解耦的关键,在当今软件工程中占有举足轻重的地位,两者之间有着密不可分的联系。今天就以一个简单的小例子,简述如何在WPF中实现依赖注入和控制反转,仅供学习分享使用,如有不足之处,还请指正。

 

什么是依赖注入和控制反转?

 

依赖注入又称为依赖项注入,那什么是依赖项呢?比如在一个类A中,实现某中功能,而此功能是另外一个类B实现的,那就说明A依赖B,B就是A的依赖项。或者是另一个对象A所依赖的对象B。示例如下:


namespaceDemoIoc{
publicclassMessageWriter    {
publicvoidPrint(stringmessage)
        {
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
        }
    }
publicclassWorker : BackgroundService    {
privatereadonlyMessageWriterwriter=new();
protectedoverrideasyncTaskExecuteAsync(CancellationTokenstoppingToken)
        {
while (!stoppingToken.IsCancellationRequested)
            {
writer.Print($"Worker running at: {DateTimeOffset.Now}");
awaitTask.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,如下所示:

usingSystem;
usingSystem.Collections.Generic;
usingSystem.Linq;
usingSystem.Text;
usingSystem.Threading.Tasks;
namespaceDemoIoc{
publicinterfaceITextService    {
publicstringGetText();
    }
publicclassTextService : ITextService    {
publicstringGetText()
        {
returnDateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
        }
    }
}

 

3. 接口注入

 

在需要调用的地方(如:MainWindow)进行ITextService接口注入,如下所示:

namespaceDemoIoc{
/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>publicpartialclassMainWindow : Window    {
privateITextServicetextService;
publicMainWindow(ITextServicetextService)
        {
this.textService=textService;
InitializeComponent();
        }
privatevoidWindow_Loaded(objectsender, RoutedEventArgse)
        {
this.txtCurrentTime.Text=textService.GetText();
        }
    }
}

注意:以上可以看出MainWindow依赖ITextService接口,而不依赖于接口的实现。这样就实现了依赖注入。

 

4. 配置容器

 

在启动程序App.xaml.cs中,添加当前对象成员,和服务提供对象,并在实例化服务对象的时候一次性注册,以便在后续需要的时候进行获取。如下所示:

namespaceDemoIoc{
/// <summary>/// Interaction logic for App.xaml/// </summary>publicpartialclassApp : Application    {
/// <summary>/// 获取当前 App 实例/// </summary>publicnewstaticAppCurrent=> (App)Application.Current;
/// <summary>/// 获取存放应用服务的容器/// </summary>publicIServiceProviderServiceProvider { get; }
publicApp()
        {
ServiceProvider=ConfigureServices();
        }
/// <summary>/// 配置应用的服务/// </summary>privatestaticIServiceProviderConfigureServices()
        {
varserviceCollection=newServiceCollection()
                .AddSingleton<ITextService,TextService>()
                .AddSingleton<MainWindow>();
returnserviceCollection.BuildServiceProvider();
        }
protectedoverridevoidOnStartup(StartupEventArgse)
        {
varmainWindow=ServiceProvider.GetRequiredService<MainWindow>();
mainWindow.Show();
        }
    }
}

注意:在此示例中,MainWindow通过服务注册的方式进行实例化,所以需要删除默认的App.xaml中StartUri属性设置,否则将提示默认构造函数不存在。

 

示例测试

 

经过上述步骤,就实现了WPF中依赖注入和控制反转,测试结果如下:

说明:正常输出,则表示依赖注入成功。

 

参考文档

 

1. .Net依赖项注入:https://learn.microsoft.com/zh-cn/dotnet/core/extensions/dependency-injection

 

以上就是依赖注入和控制反转的全部内容,希望可以抛砖引玉,一起学习,共同进步。

相关文章
|
2月前
|
设计模式 测试技术 C#
WPF/C#:在WPF中如何实现依赖注入
WPF/C#:在WPF中如何实现依赖注入
46 0
|
前端开发 C# 图形学
【.NET6+WPF】WPF使用prism框架+Unity IOC容器实现MVVM双向绑定和依赖注入
前言:在C/S架构上,WPF无疑已经是“桌面一霸”了。在.NET生态环境中,很多小伙伴还在使用Winform开发C/S架构的桌面应用。但是WPF也有很多年的历史了,并且基于MVVM的开发模式,受到了很多开发者的喜爱。
642 0
【.NET6+WPF】WPF使用prism框架+Unity IOC容器实现MVVM双向绑定和依赖注入
|
C# 图形学 容器
WPF PRISM开发入门二(Unity依赖注入容器使用)
原文:WPF PRISM开发入门二(Unity依赖注入容器使用) 这篇博客将通过一个控制台程序简单了解下PRISM下Unity依赖注入容器的使用。我已经创建了一个例子,通过一个控制台程序进行加减乘除运算,项目当中将输入输出等都用接口封装后,结构如下: 当前代码可以点击这里下载。
1927 0
|
5月前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
339 0
|
5月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
122 1
|
2月前
|
开发框架 缓存 前端开发
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(11) -- 下拉列表的数据绑定以及自定义系统字典列表控件
|
2月前
|
C# 开发者 Windows
一款基于Fluent设计风格、现代化的WPF UI控件库
一款基于Fluent设计风格、现代化的WPF UI控件库
|
2月前
|
C# 前端开发 UED
WPF数据验证实战:内置控件与自定义规则,带你玩转前端数据验证,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据验证是确保输入正确性的关键环节。前端验证能及时发现错误,提升用户体验和程序可靠性。本文对比了几种常用的WPF数据验证方法,并通过示例展示了如何使用内置验证控件(如`TextBox`)及自定义验证规则实现有效验证。内置控件结合`Validation`类可快速实现简单验证;自定义规则则提供了更灵活的复杂逻辑支持。希望本文能帮助开发者更好地进行WPF数据验证。
57 0
|
2月前
|
C# UED 定位技术
WPF控件大全:初学者必读,掌握控件使用技巧,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,控件是实现用户界面交互的关键元素。WPF提供了丰富的控件库,包括基础控件(如`Button`、`TextBox`)、布局控件(如`StackPanel`、`Grid`)、数据绑定控件(如`ListBox`、`DataGrid`)等。本文将介绍这些控件的基本分类及使用技巧,并通过示例代码展示如何在项目中应用。合理选择控件并利用布局控件和数据绑定功能,可以提升用户体验和程序性能。
47 0
|
2月前
|
C# Windows
WPF中如何使用HandyCotrol控件库
WPF中如何使用HandyCotrol控件库
115 1