码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • WPF/C#:在WPF中如何实现依赖注入


    本文先介绍依赖注入的概念,再解释为什么要进行依赖注入,最后通过 WPF Gallery 这个项目学习如何在WPF中使用依赖注入。
    合集 - C#(78)
    1.使用C#将几个Excel文件合并去重分类2023-11-152.C#使用SqlSugar操作MySQL数据库实现简单的增删改查2023-11-163.C#中的类和继承2023-11-174.C#中的virtual和override关键字2023-11-175.C#中的属性2023-11-206.C#winform中使用SQLite数据库2023-11-237.C#简化工作之实现网页爬虫获取数据2023-11-278.C#中的委托(一)2023-11-299.C#中的ref关键字2023-11-2910.C#中out关键字2023-11-2911.C#中内置的泛型委托Func与Action2023-12-0412.在winform blazor hybrid中绘图2023-12-1313.使用C#如何监控选定文件夹中文件的变动情况?2023-12-2814.C#设计模式之策略模式01-0215.由浅入深理解C#中的事件01-0416.C#设计模式之观察者模式01-0417.C#设计模式之单例模式01-0818.C#基于ScottPlot进行可视化01-1319.C#使用MiniExcel导入导出数据到Excel/CSV文件02-1020.winform实现最小化至系统托盘02-1921.C#使用Bogus生成测试数据02-2722.SemanticKernel如何基于自有数据聊天03-0523.在winform中如何嵌入第三方软件窗体✨03-0724.在winform中如何实现双向数据绑定?03-1825.自己动手做一个批量doc转换为docx文件的小工具03-2026.WPF中动画教程(DoubleAnimation的基本使用)04-0127.WPF动画教程(PointAnimationUsingPath的使用)04-0328.C#使用PaddleOCR进行图片文字识别✨04-0929.WPF基础:在Canvas上绘制图形04-1630.WPF/C#:让绘制的图形可以被选中并将信息显示在ListBox中04-1931.使用归一化盒过滤器对图像进行平滑处理04-2032.WPF/C#:如何显示具有层级关系的数据04-2233.将彩色图转化为灰度图及其原理介绍04-2334.WPF/C#:ProgressBar的基本使用05-0835.在VB.NET项目中使用C#编写的代码05-2136.WPF/C#:理解与实现WPF中的MVVM模式05-2837.SemanticKernel:添加插件06-0638.Avalonia:一个.NET跨平台UI框架06-1039.WPF/C#:异常处理06-1240.WPF/C#:程序关闭的三种模式06-1241.wpfui:一个开源免费具有现代化设计趋势的WPF控件库06-1342.WPF/C#:如何将数据分组显示06-1743.WPF/C#:更改界面的样式06-1744.LiveCharts2:简单灵活交互式且功能强大的.NET图表库06-1845.WPF/C#:显示分组数据的两种方式06-1946.WPF/C#:在DataGrid中显示选择框06-2047.WPF/C#:数据绑定到方法06-2148.WPF/C#:BusinessLayerValidation06-2549.WPF/C#:如何实现拖拉元素06-2750.WPF在.NET9中的重大更新:Windows 11 主题07-0151.ONNX Runtime入门示例:在C#中使用ResNet50v2进行图像识别07-0252.动手学Avalonia:基于SemanticKernel与硅基流动构建AI聊天与翻译工具07-0353.Avalonia应用在基于Linux的国产操作deepin上运行07-0454.如何让其他模型也能在SemanticKernel中调用本地函数07-0555.大语言模型的应用探索—AI Agent初探!07-0856.动手学Avalonia:基于硅基流动构建一个文生图应用(一)07-10
    57.WPF/C#:在WPF中如何实现依赖注入07-11
    58.ScreenToGif:一款开源免费且好用的录屏转Gif软件07-1259.WPF/C#:实现导航功能07-1860.关于学习.NET的历程回顾与今后的探索实践方向07-2461.入门Vue+.NET 8 Web Api记录(一)07-2562.SemanticKernel/C#:检索增强生成(RAG)简易实践08-0163.SemanticKernel/C#:使用Ollama中的对话模型与嵌入模型用于本地离线场景08-0264.SemanticKernel/C#:实现接口,接入本地嵌入模型08-0665.EF Core连接PostgreSQL数据库08-0766.基于SiliconCloud快速体验GraphRag.Net08-0867.AvaloniaChat:一个基于大语言模型用于翻译的简单应用08-1668.最佳实践:在AvaloniaChat中接入SiliconCloud08-1669.AvaloniaChat—从源码构建指南08-1870.SimpleRAG:基于WPF与Semantic Kernel实现的一个简单的RAG应用08-1971.Semantic Kernel/C#:接入智谱AI的两种方式08-2172.AvaloniaChat-v0.0.2:兼容智谱AI 快速使用指南08-2173.使用SiliconCloud快速体验SimpleRAG(手把手教程)08-2274.使用Ollama本地离线体验SimpleRAG(手把手教程)08-2375.Semantic Kernel/C#:一种通用的Function Calling方法,文末附经测试可用的大模型08-2976.在SimpleRAG中使用SiliconCloud快速测试Function Calling08-2977.SimpleTranslationAIAgent:基于C#与LLM的翻译AI Agent08-3178.SimpleTranslationAIAgent借助SiliconCloud API 构建自己的专属翻译助手08-31
    收起

    前言

    本文通过 WPF Gallery 这个项目学习依赖注入的相关概念与如何在WPF中进行依赖注入。

    什么是依赖注入

    依赖注入(Dependency Injection,简称DI)是一种设计模式,用于实现控制反转(Inversion of Control,简称IoC)原则。依赖注入的主要目的是将对象的创建和对象之间的依赖关系的管理从对象内部转移到外部容器或框架中,从而提高代码的可维护性、可测试性和灵活性。

    依赖注入的核心概念

    1. 依赖:一个对象需要另一个对象来完成其工作,那么前者就依赖于后者。例如,一个OrderService类可能依赖于一个ProductRepository类来获取产品信息。
    2. 注入:将依赖的对象传递给需要它的对象,而不是让需要它的对象自己去创建依赖的对象。注入可以通过构造函数、属性或方法参数来实现。
    3. 容器:一个管理对象创建和依赖关系的框架或库。容器负责实例化对象,解析依赖关系,并将依赖的对象注入到需要它们的对象中。

    依赖注入的类型

    构造函数注入:依赖的对象通过类的构造函数传递。

    public class OrderService
    {
        private readonly IProductRepository _productRepository;
    
        public OrderService(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }
    }
    

    属性注入:依赖的对象通过类的公共属性传递。

    public class OrderService
    {
        public IProductRepository ProductRepository { get; set; }
    }
    

    方法注入:依赖的对象通过类的方法参数传递。

    public class OrderService
    {
        public void ProcessOrder(IProductRepository productRepository)
        {
            // 使用 productRepository 处理订单
        }
    }
    

    为什么要进行依赖注入

    依赖注入(Dependency Injection,简称DI)是一种设计模式,通过它可以将对象的创建和对象之间的依赖关系的管理从对象内部转移到外部容器或框架中。进行依赖注入有以下几个重要的原因和优点:

    1. 降低耦合度: 依赖注入通过将依赖关系的管理从对象内部转移到外部容器,使得对象不需要知道如何创建其依赖的对象,只需要知道依赖对象的接口。这样可以显著降低对象之间的耦合度,使得代码更加模块化和灵活。
    2. 提高可测试性: 依赖注入使得单元测试变得更加容易和高效。通过使用模拟对象(Mock Object)或存根(Stub)来替代实际的依赖对象,开发者可以在不依赖于实际实现的情况下进行单元测试。这有助于确保测试的独立性和可靠性。
    3. 提高可维护性: 由于依赖注入降低了对象之间的耦合度,代码变得更加模块化和清晰。这使得代码更容易理解和维护。当需要修改或替换某个依赖对象时,只需要修改配置或注册信息,而不需要修改使用该对象的代码。
    4. 提高灵活性: 依赖注入使得系统更加灵活,能够轻松地替换依赖的对象,从而实现不同的功能或行为。例如,可以通过配置文件或代码来切换不同的数据库访问层实现,而不需要修改业务逻辑代码。
    5. 促进关注点分离: 依赖注入有助于实现关注点分离(Separation of Concerns),使得每个对象只需要关注自己的职责,而不需要关心如何创建和获取其依赖的对象。这有助于提高代码的清晰度和可维护性。
    6. 支持设计模式和最佳实践: 依赖注入是许多设计模式和最佳实践的基础,如控制反转(Inversion of Control,简称IoC)、服务定位器模式(Service Locator Pattern)等。通过使用依赖注入,开发者可以更容易地实现这些模式和实践,从而提高代码的质量和可扩展性。

    如何实现依赖注入

    本文通过 WPF Gallery 项目学习在WPF中如何使用依赖注入,代码地址:

    https://github.com/microsoft/WPF-Samples/blob/main/SampleApplications/WPFGallery

    这个项目中实现依赖注入,使用到了这两个包:

    image-20240711100435001

    首先查看App.xaml.cs中的内容:

    public partial class App : Application
    {
    
        private static readonly IHost _host = Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) =>
            {
                services.AddSingleton();
                services.AddSingleton();
                services.AddSingleton();
                
                services.AddTransient();
                services.AddTransient();
    
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
    
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
                services.AddTransient();
    
                services.AddTransient();
                services.AddTransient();
    
                services.AddTransient();
                services.AddTransient();
    
                services.AddSingleton();
                services.AddSingleton();
    
                services.AddSingleton();
                services.AddSingleton();
    
                services.AddSingleton();
                services.AddSingleton();
            }).Build();
    
    
        [STAThread]
        public static void Main()
        {
            _host.Start();
    
            App app = new();
            app.InitializeComponent();
            app.MainWindow = _host.Services.GetRequiredService();
            app.MainWindow.Visibility = Visibility.Visible;
            app.Run();
        }
    }
    

    image-20240711083011393

    IHost是什么?

    在C#中,IHost 是一个接口,它是.NET 中用于构建和配置应用程序的Host的概念的抽象。IHost接口定义了启动、运行和管理应用程序所需的服务和组件的集合。它通常用于ASP.NET Core应用程序,但也适用于其他类型的.NET 应用程序,如控制台应用程序或WPF程序。

    image-20240711082817268

    IHost接口由HostBuilder类实现,它提供了创建和配置IHost实例的方法。HostBuilder允许你添加各种服务,如日志记录、配置、依赖注入容器等,并配置应用程序的启动和停止行为。

    image-20240711083048854

    image-20240711083156306

    提供了用于使用预配置默认值创建Microsoft.Extensions.Hosting.IHostBuilder实例的方便方法。

    image-20240711083713204

    返回一个IHostBuilder。

    image-20240711084145756

    image-20240711084211035

    向容器中添加服务。此操作可以调用多次,其结果是累加的。

    参数configureDelegate的含义是配置Microsoft.Extensions.DependencyInjection.IServiceCollection的委托,
    该集合将用于构造System.IServiceProvider。

    该委托需要两个参数类型分别为HostBuilderContext、IServiceCollection没有返回值。

    image-20240711084853971

    这里传入了一个满足该委托类型的Lambda表达式。

    在C#中,() => {}是一种Lambda表达式的语法。Lambda表达式是一种轻量级的委托包装器,它可以让你定义一个匿名方法,并将其作为参数传递给支持委托或表达式树的方法。

    Lambda表达式提供了一种简洁的方式来定义方法,特别是在需要将方法作为参数传递给其他方法时,它们非常有用。

    image-20240711085344696

    在添加服务,这里出现了两种生命周期,除了AddSingleton、AddTransient外还有AddScoped。

    这些方法定义了服务的生命周期,即服务实例在应用程序中的创建和管理方式。

    AddSingleton

    • 生命周期:单例(Singleton)
    • 含义:在整个应用程序生命周期内,只创建一个服务实例。无论从容器中请求多少次,都会返回同一个实例。
    • 适用场景:适用于无状态服务,或者在整个应用程序中共享的资源,如配置、日志记录器等。

    AddTransient

    • 生命周期:瞬时(Transient)
    • 含义:每次从容器中请求服务时,都会创建一个新的实例。
    • 适用场景:适用于有状态的服务,或者每次请求都需要一个新的实例的场景,如页面、视图模型等。

    AddScoped

    • 生命周期:作用域(Scoped)
    • 含义:在每个作用域内,服务实例是唯一的。作用域通常与请求的生命周期相关联,例如在Web应用程序中,每个HTTP请求会创建一个新的作用域。
    • 适用场景:适用于需要在请求范围内共享实例的服务,如数据库上下文。

    使用这些服务

    在Main函数中:

    image-20240711095100016

    启动_host,通过_host.Services.GetRequiredService();获取MainWindow实例。

    以MainWindow类为例,查看MainWindow.xaml.cs中MainWindow的构造函数:

    public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService)
    {
        _serviceProvider = serviceProvider;
        ViewModel = viewModel;
        DataContext = this;
        InitializeComponent();
    
        Toggle_TitleButtonVisibility();
    
        _navigationService = navigationService;
        _navigationService.Navigating += OnNavigating;
        _navigationService.SetFrame(this.RootContentFrame);
        _navigationService.Navigate(typeof(DashboardPage));
    
        WindowChrome.SetWindowChrome(
            this,
            new WindowChrome
            {
                CaptionHeight = 50,
                CornerRadius = default,
                GlassFrameThickness = new Thickness(-1),
                ResizeBorderThickness = ResizeMode == ResizeMode.NoResize ? default : new Thickness(4),
                UseAeroCaptionButtons = true
            }
        );
    
        this.StateChanged += MainWindow_StateChanged;
    }
    

    去掉与本主题无关的内容之后,如下所示:

    public MainWindow(MainWindowViewModel viewModel, IServiceProvider serviceProvider, INavigationService navigationService)
    {
        _serviceProvider = serviceProvider;
        ViewModel = viewModel; 
        _navigationService = navigationService;  
    }
    

    有没有发现不用自己new这些对象了,这些对象的创建由依赖注入容器来管理,在需要这些对象的时候,像现在这样通过构造函数中注入即可。

    如果没有用依赖注入,可能就是这样子的:

    public MainWindow()
    {
        _serviceProvider = new IServiceProvider();
        ViewModel = new MainWindowViewModel(); 
        _navigationService = new INavigationService();  
    }
    

    总结

    本文先介绍依赖注入的概念,再解释为什么要进行依赖注入,最后通过 WPF Gallery 这个项目学习如何在WPF中使用依赖注入。

    参考

    1、https://github.com/microsoft/WPF-Samples/tree/main/SampleApplications/WPFGallery

  • 相关阅读:
    YOLOv5_Android_USBCamera:支持USB摄像头的YOLOv5Android图像识别项目
    Android EventBus 事件订阅/发布框架
    数据结构--顺序表
    【开始刷题啦——Leetcode《初级算法》(Go语言)】
    电子票据:安全便捷的存储与查看方案,让你随时随地管理票据
    FPGA零基础学习:半导体存储器和可编程逻辑器件简介
    Java中next()和nextLine()的区别(为什么nextLine()输入回车没显示)
    内存快照:宕机后,Redis如何实现快速恢复?
    期货开户手续费加一分是主流
    【8】c++设计模式——>单一职责原则
  • 原文地址:https://www.cnblogs.com/mingupupu/p/18295546
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号