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();

        // 注册应用中的服务和ViewModel
        services.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方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/546157.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

携程景点详情API:电商发展新引擎,推动旅游智能化升级

随着信息技术的快速发展&#xff0c;旅游行业正迎来一场深刻的智能化升级。作为电商发展的新引擎&#xff0c;携程景点详情API以其丰富的数据资源和高效的服务能力&#xff0c;正逐渐成为推动旅游智能化升级的重要力量。本文将深入探讨携程景点详情API在电商发展中的作用&#…

xhci 数据结构

xhci 数据结构 xhci 数据结构主要在手册上有详细的定义&#xff0c;本文根据手册进行归纳总结&#xff1a; 重点关注的包括&#xff1a; device contexttrb ringtrb device context设备上下文 设备上下文数据结构由xHC管理&#xff0c;用于向系统软件报告设备配置和状态信息。…

【个人博客搭建】(2)项目分层结构

1、在解决方案这右击&#xff0c; 2、填写项目名称。&#xff08;位置使用默认即可&#xff09; 3、选择框架版本。&#xff08;最好同创建webapi一个版本吧&#xff09; 4、创建后进入该界面。会生成默认的一个Class类。&#xff08;后修改名称或删除都可&#xff09; 5、然后…

【C/C++】什么是内存泄漏?如何检测内存泄漏?

一、内存泄漏概述 1.1 什么是内存泄漏 内存泄漏是在没有自动 gc 的编程语言里面&#xff0c;经常发生的一个问题。 自动垃圾回收&#xff08;Automatic Garbage Collection&#xff0c;简称 GC&#xff09;是一种内存管理技术&#xff0c;在程序运行时自动检测和回收不再使用…

MySQL进阶-----limit、count、update优化

目录 前言 一、limit优化 1. 未优化案例 2.优化后案例 二、count优化 count用法 三、update优化 1.锁行情况&#xff08;有索引&#xff09; 2.锁表情况&#xff08;无索引&#xff09; 前言 上一期我们学习了order by优化和group by优化&#xff0c;本期我们就继续学习…

不需要GPU就可以玩转模型,同时支持本地化部署

简单一款不需要GPU就可以在Win 机器跑的模型&#xff1a;Ollama&#xff1b;用于本地运行和部署大型语言模型&#xff08;LLMs&#xff09;的开源工具 关于Ollama的简要介绍 平台兼容性&#xff1a;Ollama支持多种操作系统&#xff0c;包括macOS、Linux和Windows&#xff0c;…

Spectre漏洞 v2 版本再现,影响英特尔 CPU + Linux 组合设备

近日&#xff0c;网络安全研究人员披露了针对英特尔系统上 Linux 内核的首个原生 Spectre v2 漏洞&#xff0c;该漏洞是2018 年曝出的严重处理器“幽灵”&#xff08;Spectre&#xff09;漏洞 v2 衍生版本&#xff0c;利用该漏洞可以从内存中读取敏感数据&#xff0c;主要影响英…

一维非线性扩展卡尔曼滤波|matlab的EKF程序|一维例程源代码

为了满足不同条件下的用途,编了一个简单的一维状态量下的EKF,后面准备出UKF和CKF的版本。 使用的系统是非线性的,以体现算法对于非线性系统的性能。(状态方程和观测方程均设计成非线性的) 程序运行截图 程序都在一个m文件里面,粘贴到matlab的编辑器就能运行,如果中文注…

vivado 写入 ILA 探针信息、读取 ILA 探针信息

写入 ILA 探针信息 “调试探针 (Debug Probes) ”窗口中的“ ILA 核 (ILA Cores) ”选项卡视图包含有关您在自己的设计中使用 ILA 核探测的 信号线的信息。此 ILA 探针信息提取自您的设计 &#xff0c; 并存储在数据文件内 &#xff0c; 此数据文件通常带有 .ltx 文件扩…

React 集成三方登录按钮样式的插件库

按钮不提供任何社交逻辑。 效果如下&#xff1a; 原地址&#xff1a;https://www.npmjs.com/package/react-social-login-buttons 时小记&#xff0c;终有成。

基于注解以及配置类使用SpringIoc

四 基于注解方式使用SpringIoc 和 XML 配置文件一样&#xff0c;注解本身并不能执行&#xff0c;注解本身仅仅只是做一个标记&#xff0c;具体的功能是框架检测到注解标记的位置&#xff0c;然后针对这个位置按照注解标记的功能来执行具体操作。 本质上&#xff1a;所有一切的…

UML简单小结

文章目录 一、UML概述二、UML建模工具三、类图1、概念2、组成 四、类与类之间的关系1、继承2、实现3、依赖4、关联5、聚合6、组合 五、常见UML图1、用例图1&#xff09; 概念2&#xff09;组成3&#xff09;用例图所包含的的关系关联(Association)泛化(Inheritance)包含(Includ…

web笔记再整理

前四天笔记在此连接: web前端笔记表单练习题五彩导航栏练习题-CSDN博客https://blog.csdn.net/simply_happy/article/details/136917265?spm1001.2014.3001.5502 # 1.边框弧度​ div {​ width: 300px;​ height: 50px;​ background-color: aqua;​ …

sql注入之时间注入

一、时间注入 时间注入又名延时注入&#xff0c;属于盲注入的一种&#xff0c;通常是某个注入点无法通过布尔型注入获取数据&#xff0c;而采用一种突破注入的技巧。 在 mysql 里 函数 sleep() 是延时的意思&#xff0c;sleep(10)就是数据库延时 10 秒返回内容。判断注入可以使…

G2D图像处理硬件调用和测试-基于米尔-全志T113-i开发板

本篇测评由电子工程世界的优秀测评者“jf_99374259”提供。 本文将介绍基于米尔电子MYD-YT113i开发板的G2D图像处理硬件调用和测试。 MYC-YT113i核心板及开发板 真正的国产核心板&#xff0c;100%国产物料认证 国产T113-i处理器配备2*Cortex-A71.2GHz &#xff0c;RISC-V 外置…

Selenium自动化测试网页加载太慢如何解决?

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 遇到网页加载慢&#xff0c;selenium运行效率降低&#xff0c;可以通过修改页面加载策略提升自动…

docker-compose yaml指定具体容器网桥ip网段subnet;docker创建即指定subnet;docker取消自启动

1、docker-compose yaml指定具体容器网桥ip网段subnet docker-compose 启动yaml有时可能的容器网段与宿主机的ip冲突导致宿主机上不了网&#xff0c;这时候可以更改yaml指定subnet 宿主机内网一般是192**&#xff0c;这时候容器可以指定172* version: 3.9 services:coredns:…

Django之rest_framework(四)

扩展的视图类介绍 rest_framework提供了几种后端视图(对数据资源进行增删改查)处理流程的实现,如果需要编写的视图属于这几种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量 官网:3 - Class based views - Django REST framework rest_framework.mixi…

比特币突然暴跌

作者&#xff1a;秦晋 周末愉快。 今天给大家分享两则比特币新闻&#xff0c;也是两个数据。一则是因为中东地缘政治升温&#xff0c;传统资本市场的风险情绪蔓延至加密市场&#xff0c;引发加密市场暴跌。比特币跌至66000美元下方。杠杆清算金额高达8.5亿美元。 二则是&#x…

【Node.js】Express学习笔记(黑马)

目录 初识 ExpressExpress 简介Express 的基本使用托管静态资源nodemon Express 路由路由的概念路由的使用 Express 中间件中间件的概念Express 中间件的初体验中间件的分类 初识 Express Express 简介 什么是 Express&#xff1f; 官方给出的概念&#xff1a;Express 是基于…