CommunityToolkit.Mvvm笔记---Ioc

2024-04-16 07:28

本文主要是介绍CommunityToolkit.Mvvm笔记---Ioc,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

使用MVVM模式提高应用程序代码库中的模块化程度的最常用模式是使用某种形式的反转控制(Ioc)。其中最常见的解决方案是使用依赖关系注入,该解决方案存在于创建多个注入后端类的服务(即以参数的形式传递给 viewmodel 构造函数)的过程中,这允许使用这些服务的代码不依赖这些服务的实现详细信息,并且也可以轻松地交换这些服务的具体实现。 这种模式还可以通过服务将特定于平台的功能抽象出来,然后在需要的地方注入这些功能,从而使后端代码可以轻松使用这些功能。

MVVM工具包没提供内置的API来促进这种模式的使用,因为已经有专用库(Microsoft.Extensions.DependencyInjection 包),本文中示例都是参考此库。

什么是依赖注入

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

public class MyDependency
{public void WriteMessage(string message){Console.WriteLine($"MyDependency.WriteMessage called. Message: {message}");}
}

类可以创建 MyDependency 类的实例,以便利用其 WriteMessage 方法。 在以下示例中,MyDependency 类是 IndexModel 类的依赖项 

public class IndexModel : PageModel
{private readonly MyDependency _dependency = new MyDependency();public void OnGet(){_dependency.WriteMessage("IndexModel.OnGet");}
}

注意:在上述示例中,MyDependency 类依赖于IndexModel 类,所以IndexModel 就是MyDependency 的依赖项。 硬编码的依赖项(如前面的示例)会产生问题,应避免使用。

强依赖关系具有以下几个问题:

  • 如果要用不同的实现替换 MyDependency ,必须修改 IndexModel 类。
  • 如果 MyDependency 具有依赖项,则必须由 IndexModel 类对其进行配置,且很难进行初始化。
  • 这种实现很难进行单元测试。

那如何解决上述依赖关系所造成的弊端呢?答案就是依赖项注入。可通过如下几个步骤实现:

  • 使用接口或基类将依赖关系实现抽象化。
  • 在服务容器中注册依赖关系。
  • 将服务注入到使用它的类的构造函数中。

 .NET 提供了一个内置的服务容器 IServiceProvider。 服务通常在应用启动时注册,并追加到 IServiceCollection。 添加所有服务后,可以使用 BuildServiceProvider 创建服务容器。 框架负责创建依赖关系的实例,并在不再需要时将其释放。

简单一句话说:依赖注入(DI)将所依赖的对象参数化,接口化,并且将依赖对象的创建和释放剥离出来,这样就做到了解耦,并且实现了控制反转(IoC)

控制反转(IoC)具有如下两个特点:

  • 高等级的代码不能依赖低等级的代码;
  • 抽象接口不能依赖具体实现;

控制反转解决代码的强耦合,增加了代码的可扩展性。依赖注入将依赖具体实现类和控制实现类的创建和释放,变成了依赖接口或抽象类,不再控制接口的创建和释放。两者之间相辅相成,互相成就。

WPF依赖注入示例

步骤 1: 设置项目和安装必要的NuGet包

Install-Package Microsoft.Extensions.DependencyInjection

 或者

步骤 2: 创建依赖注入容器

创建一个静态类来构建和存储IServiceProvider实例。这个类将负责配置服务和解析依赖。

创建 DependencyInjection.cs
using Microsoft.Extensions.DependencyInjection;
using System;public static class DependencyInjection
{private static IServiceProvider serviceProvider;public static void ConfigureServices(){var services = new ServiceCollection();// 注册应用中的服务和ViewModelservices.AddSingleton<MainWindow>();services.AddTransient<IMyService, MyService>();services.AddTransient<MainViewModel>();serviceProvider = services.BuildServiceProvider();}public static T GetService<T>(){return serviceProvider.GetService<T>();}
}

在这个类中,我们使用了Microsoft.Extensions.DependencyInjection来创建服务集合,然后构建IServiceProvider

步骤 3: 配置主窗口和ViewModel

修改你的MainWindow,使其可以接收依赖(比如MainViewModel

MainWindow.xaml.cs
public partial class MainWindow : Window
{public MainWindow(MainViewModel viewModel){InitializeComponent();DataContext = viewModel;}
}

步骤 4: 配置 App.xaml.cs

重写App.xaml.cs中的启动逻辑,使用依赖注入初始化MainWindow

App.xaml.cs
using System.Windows;public partial class App : Application
{protected override void OnStartup(StartupEventArgs e){base.OnStartup(e);DependencyInjection.ConfigureServices();var mainWindow = DependencyInjection.GetService<MainWindow>();mainWindow.Show();}
}

这里,应用启动时会配置服务,并从服务提供者中获取MainWindow的实例。

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

步骤 5: 创建服务和ViewModel

定义服务接口和实现,以及ViewModel。

IService 和 Service 实现
public interface IMyService
{string GetData();
}public class MyService : IMyService
{public string GetData(){return "Hello from MyService!";}
}

 ViewModel 实现

public class MainViewModel
{public string Data { get; }public MainViewModel(IMyService myService){Data = myService.GetData();}
}

步骤 6: xaml视图

<Grid><TextBlock Text="{Binding Data}"></TextBlock><Frame x:Name="MainFrame" Source="Views/Pages/RegistrationForm.xaml"  NavigationUIVisibility="Hidden" ></Frame>
</Grid>

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

 题外话:生命周期和存储方式小知识

在WPF应用程序中,通过依赖注入(DI)获取的对象(例如,通过重写OnStartup方法并从IServiceProvider获取的实例)通常不会“自动消失”或自动释放,除非你的实现或应用程序逻辑中有特定的处理使其被释放或被垃圾回收。

生命周期和存储方式

对象的持久性和存储方式主要取决于如何在依赖注入容器中注册这些对象:

  1. 单例(Singleton):只创建一个实例,该实例在应用程序的整个生命周期内持续存在。例如,注册为单例的MainWindow,在应用程序关闭前,其实例会一直存在。

  2. 瞬态(Transient):每次请求都创建一个新的实例。这意味着每次从IServiceProvider获取时都会创建一个新的对象。

  3. 作用域(Scoped):在.NET Core的Web应用中常用,每个请求创建一个新的实例,但在WPF中通常使用单例或瞬态替代。

在上述示例中,MainWindow如果注册为单例,则在应用程序关闭之前始终存在。如果注册为瞬态,那么每次调用GetService<MainWindow>()时都会创建一个新的MainWindow实例。

生命周期管理

  • 引用保持:为了确保通过依赖注入获取的对象不会“消失”,你需要保持对这些对象的引用。在WPF中,通常的做法是保持对主窗口或核心服务的引用直到应用程序关闭。
  • 释放资源:对于使用了资源较多的服务或对象,如数据库连接或文件句柄,应确保适当地管理其生命周期。这可能需要实现IDisposable接口,并在适当的时候(如窗口关闭或对象不再需要时)调用Dispose方法。

这篇关于CommunityToolkit.Mvvm笔记---Ioc的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/908147

相关文章

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

论文阅读笔记: Segment Anything

文章目录 Segment Anything摘要引言任务模型数据引擎数据集负责任的人工智能 Segment Anything Model图像编码器提示编码器mask解码器解决歧义损失和训练 Segment Anything 论文地址: https://arxiv.org/abs/2304.02643 代码地址:https://github.com/facebookresear

数学建模笔记—— 非线性规划

数学建模笔记—— 非线性规划 非线性规划1. 模型原理1.1 非线性规划的标准型1.2 非线性规划求解的Matlab函数 2. 典型例题3. matlab代码求解3.1 例1 一个简单示例3.2 例2 选址问题1. 第一问 线性规划2. 第二问 非线性规划 非线性规划 非线性规划是一种求解目标函数或约束条件中有一个或几个非线性函数的最优化问题的方法。运筹学的一个重要分支。2

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个

查看提交历史 —— Git 学习笔记 11

查看提交历史 查看提交历史 不带任何选项的git log-p选项--stat 选项--pretty=oneline选项--pretty=format选项git log常用选项列表参考资料 在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的 工具是 git log 命令。 接下来的例子会用一个用于演示的 simplegit

记录每次更新到仓库 —— Git 学习笔记 10

记录每次更新到仓库 文章目录 文件的状态三个区域检查当前文件状态跟踪新文件取消跟踪(un-tracking)文件重新跟踪(re-tracking)文件暂存已修改文件忽略某些文件查看已暂存和未暂存的修改提交更新跳过暂存区删除文件移动文件参考资料 咱们接着很多天以前的 取得Git仓库 这篇文章继续说。 文件的状态 不管是通过哪种方法,现在我们已经有了一个仓库,并从这个仓

忽略某些文件 —— Git 学习笔记 05

忽略某些文件 忽略某些文件 通过.gitignore文件其他规则源如何选择规则源参考资料 对于某些文件,我们不希望把它们纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。通常它们都是些自动生成的文件,比如日志文件、编译过程中创建的临时文件等。 通过.gitignore文件 假设我们要忽略 lib.a 文件,那我们可以在 lib.a 所在目录下创建一个名为 .gi

取得 Git 仓库 —— Git 学习笔记 04

取得 Git 仓库 —— Git 学习笔记 04 我认为, Git 的学习分为两大块:一是工作区、索引、本地版本库之间的交互;二是本地版本库和远程版本库之间的交互。第一块是基础,第二块是难点。 下面,我们就围绕着第一部分内容来学习,先不考虑远程仓库,只考虑本地仓库。 怎样取得项目的 Git 仓库? 有两种取得 Git 项目仓库的方法。第一种是在本地创建一个新的仓库,第二种是把其他地方的某个

Git 的特点—— Git 学习笔记 02

文章目录 Git 简史Git 的特点直接记录快照,而非差异比较近乎所有操作都是本地执行保证完整性一般只添加数据 参考资料 Git 简史 众所周知,Linux 内核开源项目有着为数众多的参与者。这么多人在世界各地为 Linux 编写代码,那Linux 的代码是如何管理的呢?事实是在 2002 年以前,世界各地的开发者把源代码通过 diff 的方式发给 Linus,然后由 Linus