深入理解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.hqwc.cn/news/164585.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA高端项目:图像缩放+GTP+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持

目录 1、前言免责声明本项目特点 2、相关方案推荐我这里已有的 GT 高速接口解决方案我这里已有的以太网方案我这里已有的图像处理方案 3、设计思路框架设计框图视频源选择ADV7611 解码芯片配置及采集动态彩条跨时钟FIFO图像缩放模块详解设计框图代码框图2种插值算法的整合与选择…

数据结构与算法C语言版学习笔记(2)-线性表、顺序存储结构的线性表

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 数据结构部分的知识框架一、线性表的定义和特点1.定义2.特点 二、线性表的实际案例引入1.案例一&#xff1a;多项式的加减乘除2.案例二&#xff1a;当多项式是稀疏多…

Spring Boot整合Redis实现订单超时处理

文章目录 为什么使用Redis&#xff1f;准备工作创建订单实体类存储订单到Redis设置订单超时监控订单超时订阅订单超时消息总结 &#x1f389;欢迎来到架构设计专栏~Spring Boot整合Redis实现订单超时处理 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;I…

使用Postman工具做接口测试 —— 环境变量与请求参数格式!

引言 在上一篇笔记我们主要介绍了接口测试的基础知识与基本功能&#xff0c;本章主要介绍如何使用postman做接口测试。 配置环境变量和全局变量 环境变量和全局变量 环境管理中还可以点击“Global”添加全局变量&#xff0c;环境变量只有当选择了该环境时才生效&#xff0c;…

Git设置显示中文

git config --global i18n.comitencoding utf-8 git config --global i18n.logoutputencoding utf-8 export LESSCHARSETutf-8

mysql之高阶语句

1、使用select语句&#xff0c;用order by对表进行排序【尽量用数字列进行排序】 select id,name,score from info order by score desc; ASC升序排列&#xff08;默认&#xff09; DESC降序排列&#xff08;需要添加&#xff09; &#xff08;1&#xff09;order by结合whe…

Halcon WPF 开发学习笔记(0):开篇介绍

文章目录 文章专栏Halcon是什么&#xff1f;安装教学视频链接简单来说 Halcon快速开发环境确认新建项目 文章专栏 Halcon开发 Halcon是什么&#xff1f; 史上最全VisionPro和Halcon 的详细对比 Halcon简述 Halcon基础大全&#xff08;基础算子、高阶算子、数组、分割、字符检测…

[LeetCode]-138. 随机链表的复制

目录 题目 解题步骤 1.拷贝节点插入原节点的后面 2.置每个拷贝节点random 3.拷贝节点解下来&#xff0c;尾插到一起&#xff0c;恢复原链表 完整代码 题目 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表…

Redis中的渐进式遍历-Scan命令

之前我们学习过遍历命令keys,而keys *是一次性的把整个redis中所有的key都获取到.在不知道当前redis中有多少key的情况下,这个操作是非常危险的,可能会一下子得到太多的key而阻塞redis服务器.从而使其他redis客户端卡顿. 通过渐进式遍历,就可以做到,既可以获取到所有的key,同时…

linux安装kafka教程

kafka需要安装jdk&#xff0c;我的是jdk17 一、安装kafka 1、下载kafka 1.到kafka的官网&#xff0c;去下载想用的kafka包&#xff1a;http://kafka.apache.org/downloads 2.我这里下载的是&#xff1a;kafka_2.12-3.4.1.tgz 3.将安装包传送到服务器并解压&#xff08;默认…

房产网小程序源码 房产中介小程序源码 房产网源码

房产网小程序源码 房产中介小程序源码 房产网源码 功能介绍&#xff1a; 一、付费发布信息 支持付费发布、刷新、置顶房源信息&#xff1b;二、个人发布信息 支持个人和房产经纪人发布房源信息&#xff1b;三、新房楼盘模块 支持新房楼盘功能&#xff0c;后台添加…

0X03

红包题第二弹 看到源码里面的提示 ?cmdphpinfo(); 看到源码 kk 关键点就是有两个正则表达式 第一个 preg_match("/[A-Za-oq-z0-9$]/",$cmd) 第二个 preg_match("/\~|\!|\|\#|\%|\^|\&|\*|\(|\)|\&#xff08;|\&#xff09;|\-|\_|\{|\}|\[|\]|\|\&q…