首页 > 其他分享 >深入理解WPF中的依赖注入和控制反转

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

时间:2023-11-04 13:00:11浏览次数:46  
标签:依赖 MessageWriter 反转 App 接口 WPF public 注入

在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

 

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

标签:依赖,MessageWriter,反转,App,接口,WPF,public,注入
From: https://www.cnblogs.com/hsiang/p/17806755.html

相关文章

  • node版本依赖报错那些事儿
    前言昨天还能跑的的项目,今天就歇菜了第一步删除大法依赖问题,那就把.lock/node_modules删除重新装第二步如果重装不好使,那就各种降低版本尝试遇到这样的有两种方式1、npmi--force/npmi--legacy-per-deps 2、一开始我用了第一种!但是有各......
  • pip安装依赖失败
    报错如下Requirementalreadysatisfied:pipinc:\users\ychen\appdata\local\programs\python\python310\lib\site-packages(22.0.4)CollectingpipDownloadingpip-23.3.1-py3-none-any.whl(2.1MB)---------------------------------------0.2/2.1......
  • Spring,依赖注入
    一、首先创建一个拥有各种类型的实体类,添加构造方法,get&set还有toString方法 二、在配置文件中实例化对象,并且赋值  每种类型都有自己的赋值方式 三、测试之后也可以得到我们赋值结果,JSON格式的 ......
  • Java面试题:链表-反转链表
    问题描述给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。如当输入链表{1,2,3}时,经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。示例输入:{1,2,3}返回值:{3,2,1}原题地址:https://www.nowcoder.com/practice/7......
  • WPF InputHitTest的用法
    一、定义InputHitTest是一个用于检测鼠标指针是否命中某个可视元素的方法。它可以返回命中的最上层元素。 二、用法:①首先,我们需要获取InputHitTest方法所需的参数,即鼠标指针的位置。可以通过Mouse类的GetPosition方法来获取当前鼠标指针的位置:Pointposition=Mouse.Ge......
  • 岛形反转
    岛形反转1.长什么样1.1顶部岛形反转图例说明:筑顶了,判断某个股票在某个位置,是否出现顶部形态像A字,左侧有一个跳空高开的缺口,右侧有一个跳空低开的缺口。跳空高开缺口当天并没有大涨,出现了冲高回落收出阴线,右侧出现向下低空缺口,1.2底部岛形反转图例说明:筑底了,判断某......
  • 第 365 场周赛 (依赖树, 环形滑动窗口,内向基环树)
    本题可以发现一些枚举的技巧,在枚举多个值的时候,自己有时候脑袋晕晕的,会把变量的更新顺序搞混,此时,可以用依赖树来解决。如同本题:classSolution:defmaximumTripletValue(self,nums:List[int])->int:res=pre_max=pre_dif=0forxinnums:......
  • 前端package文件依赖版本号^符号
    在`package.json`文件中,版本号前面的`^`符号表示允许自动更新到当前主版本号下的次版本号。例如,如果当前主版本号是1,次版本号是0,那么`^1.0.0`将允许安装`1.x.x`范围内的任何版本,但不会安装2.x.x或更高版本的包。......
  • Util应用框架基础(一) - 依赖注入
    本节介绍Util应用框架依赖注入的使用和配置扩展.文章分为多个小节,如果对设计原理不感兴趣,只需阅读基础用法部分即可.概述当你想调用某个服务的方法完成特定功能时,首先需要得到这个服务的实例.最简单的办法是直接new一个服务实例,不过这样就把服务的实现牢牢绑死了,当你需......
  • wpf之样式
    在Window.Resources中书写样式;<Window.Resources><StyleTargetType="Button"></Style></Window.Resources>TargetType是指定标签的类型;<StyleTargetType="Button"></Style>中的style样式会在所有的button按钮中生效;<......