最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C# 中依賴注入(DI)的實(shí)現(xiàn)方式

 更新時(shí)間:2025年11月17日 10:28:16   作者:YuanlongWang  
在C#中依賴注入(DI)的實(shí)現(xiàn)方式主要分為手動(dòng)注入和通過IoC容器注入,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前面介紹過 什么是控制反轉(zhuǎn)(IoC)?什么是依賴注入(DI)?以及實(shí)現(xiàn)原理

在 C# 中,依賴注入(DI)的實(shí)現(xiàn)方式主要分為手動(dòng)注入通過 IoC 容器注入(如 .NET 自帶的 Microsoft.Extensions.DependencyInjection)。以下是具體代碼示例,涵蓋常用場(chǎng)景和最佳實(shí)踐。

一、手動(dòng)依賴注入(基礎(chǔ)示例)

手動(dòng)注入不依賴第三方容器,直接通過代碼傳遞依賴,適合簡(jiǎn)單場(chǎng)景,核心是構(gòu)造函數(shù)注入(最推薦的方式)。

1. 構(gòu)造函數(shù)注入(推薦)

using System;

// 1. 定義抽象依賴(接口)
public interface IMessageSender
{
    void Send(string message);
}

// 2. 實(shí)現(xiàn)具體依賴(郵件發(fā)送)
public class EmailSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine($"[郵件發(fā)送] {message}");
    }
}

// 3. 實(shí)現(xiàn)具體依賴(短信發(fā)送)
public class SmsSender : IMessageSender
{
    public void Send(string message)
    {
        Console.WriteLine($"[短信發(fā)送] {message}");
    }
}

// 4. 依賴方(通知服務(wù)):通過構(gòu)造函數(shù)接收依賴
public class NotificationService
{
    private readonly IMessageSender _messageSender;

    // 構(gòu)造函數(shù)注入:依賴由外部傳入,且用 readonly 確保不可變
    public NotificationService(IMessageSender messageSender)
    {
        _messageSender = messageSender ?? throw new ArgumentNullException(
            nameof(messageSender), "依賴不能為空"); // 校驗(yàn)依賴,避免空引用
    }

    public void NotifyUser(string username)
    {
        _messageSender.Send($"用戶 {username} 已收到通知");
    }
}

// 5. 調(diào)用:手動(dòng)注入依賴
class Program
{
    static void Main()
    {
        // 手動(dòng)創(chuàng)建依賴實(shí)例
        IMessageSender emailSender = new EmailSender();
        // IMessageSender smsSender = new SmsSender(); // 可切換為短信發(fā)送

        // 注入到依賴方
        NotificationService notificationService = new NotificationService(emailSender);
        notificationService.NotifyUser("張三"); 
        // 輸出:[郵件發(fā)送] 用戶 張三 已收到通知
    }
}

優(yōu)勢(shì)

  • 依賴在對(duì)象創(chuàng)建時(shí)就必須傳入,確保對(duì)象初始化后即可正常工作(避免空引用)。
  • 依賴不可變(readonly),避免運(yùn)行時(shí)被篡改。

2. 屬性注入(不推薦,僅特殊場(chǎng)景使用)

屬性注入通過公共屬性傳遞依賴,適合 “可選依賴”(非必須的功能),但可能導(dǎo)致對(duì)象創(chuàng)建后依賴未初始化的問題。

public class NotificationService
{
    // 屬性注入:依賴通過屬性設(shè)置(通常有默認(rèn)值或允許為null)
    public IMessageSender MessageSender { get; set; } = new EmailSender(); // 默認(rèn)值

    public void NotifyUser(string username)
    {
        if (MessageSender == null)
            throw new InvalidOperationException("未設(shè)置消息發(fā)送器");
        
        MessageSender.Send($"用戶 {username} 已收到通知");
    }
}

// 調(diào)用
class Program
{
    static void Main()
    {
        var service = new NotificationService();
        service.MessageSender = new SmsSender(); // 通過屬性注入依賴
        service.NotifyUser("李四"); 
        // 輸出:[短信發(fā)送] 用戶 李四 已收到通知
    }
}

注意:屬性注入可能導(dǎo)致 “對(duì)象已創(chuàng)建但依賴未設(shè)置” 的風(fēng)險(xiǎn),除非有明確理由(如框架限制),否則優(yōu)先用構(gòu)造函數(shù)注入。

二、使用 .NET 自帶的 DI 容器(Microsoft.Extensions.DependencyInjection)

在 .NET Core/.NET 5+ 中,官方提供了 Microsoft.Extensions.DependencyInjection 容器,是企業(yè)級(jí)開發(fā)的首選。需先通過 NuGet 安裝包(一般項(xiàng)目默認(rèn)已引用):Install-Package Microsoft.Extensions.DependencyInjection

1. 基本用法(注冊(cè) + 解析)

using Microsoft.Extensions.DependencyInjection;
using System;

// 復(fù)用上面的 IMessageSender、EmailSender、SmsSender、NotificationService

class Program
{
    static void Main()
    {
        // 1. 創(chuàng)建服務(wù)容器
        var serviceCollection = new ServiceCollection();

        // 2. 注冊(cè)服務(wù)(關(guān)鍵步驟:告訴容器“抽象 -> 具體實(shí)現(xiàn)”的映射)
        // 注冊(cè) IMessageSender,指定實(shí)現(xiàn)為 EmailSender
        serviceCollection.AddSingleton<IMessageSender, EmailSender>();
        // 注冊(cè) NotificationService(容器會(huì)自動(dòng)注入其依賴 IMessageSender)
        serviceCollection.AddSingleton<NotificationService>();

        // 3. 構(gòu)建服務(wù)提供器(容器的具體實(shí)現(xiàn))
        var serviceProvider = serviceCollection.BuildServiceProvider();

        // 4. 從容器解析服務(wù)(自動(dòng)處理依賴鏈)
        var notificationService = serviceProvider.GetRequiredService<NotificationService>();

        // 5. 使用服務(wù)
        notificationService.NotifyUser("王五"); 
        // 輸出:[郵件發(fā)送] 用戶 王五 已收到通知
    }
}

2. 服務(wù)生命周期(3 種核心類型)

容器通過 “生命周期” 管理服務(wù)實(shí)例的創(chuàng)建和銷毀,核心有 3 種:

生命周期說明適用場(chǎng)景
Transient每次請(qǐng)求(GetService)創(chuàng)建新實(shí)例輕量級(jí)、無(wú)狀態(tài)服務(wù)(如工具類)
Scoped每個(gè) “作用域” 內(nèi)創(chuàng)建一個(gè)實(shí)例(如 Web 請(qǐng)求)數(shù)據(jù)庫(kù)上下文(DbContext)
Singleton整個(gè)應(yīng)用生命周期內(nèi)只創(chuàng)建一個(gè)實(shí)例全局配置、緩存服務(wù)

示例:驗(yàn)證生命周期差異

using Microsoft.Extensions.DependencyInjection;
using System;

// 測(cè)試服務(wù):記錄實(shí)例ID,觀察是否為同一實(shí)例
public class TestService
{
    public Guid Id { get; } = Guid.NewGuid(); // 實(shí)例化時(shí)生成唯一ID
}

class Program
{
    static void Main()
    {
        var services = new ServiceCollection();

        // 注冊(cè)3種生命周期的服務(wù)
        services.AddTransient<TestService>(); // Transient
        services.AddScoped<TestService>();    // Scoped(需在作用域內(nèi)解析)
        services.AddSingleton<TestService>(); // Singleton

        var provider = services.BuildServiceProvider();

        // 1. 測(cè)試 Transient:每次獲取都是新實(shí)例
        Console.WriteLine("Transient:");
        var t1 = provider.GetRequiredService<TestService>();
        var t2 = provider.GetRequiredService<TestService>();
        Console.WriteLine($"t1.Id == t2.Id? {t1.Id == t2.Id}"); // 輸出:False

        // 2. 測(cè)試 Scoped:同一作用域內(nèi)是同一實(shí)例,不同作用域不同
        Console.WriteLine("\nScoped:");
        using (var scope1 = provider.CreateScope()) // 創(chuàng)建作用域1
        {
            var s1 = scope1.ServiceProvider.GetRequiredService<TestService>();
            var s2 = scope1.ServiceProvider.GetRequiredService<TestService>();
            Console.WriteLine($"scope1內(nèi) s1.Id == s2.Id? {s1.Id == s2.Id}"); // True
        }
        using (var scope2 = provider.CreateScope()) // 創(chuàng)建作用域2
        {
            var s3 = scope2.ServiceProvider.GetRequiredService<TestService>();
            Console.WriteLine($"scope2內(nèi) s3.Id 與 scope1的s1不同? {true}"); // 必然不同
        }

        // 3. 測(cè)試 Singleton:全局唯一實(shí)例
        Console.WriteLine("\nSingleton:");
        var s1 = provider.GetRequiredService<TestService>();
        var s2 = provider.GetRequiredService<TestService>();
        Console.WriteLine($"s1.Id == s2.Id? {s1.Id == s2.Id}"); // 輸出:True
    }
}

3. 依賴鏈解析(多層依賴)

容器會(huì)自動(dòng)解析 “依賴的依賴”(遞歸解析),無(wú)需手動(dòng)處理多層依賴關(guān)系。

using Microsoft.Extensions.DependencyInjection;
using System;

// 第一層依賴:日志服務(wù)
public interface ILogger { void Log(string msg); }
public class ConsoleLogger : ILogger { public void Log(string msg) => Console.WriteLine($"[日志] {msg}"); }

// 第二層依賴:用戶倉(cāng)儲(chǔ)(依賴日志)
public interface IUserRepository 
{ 
    void Add(string username); 
}
public class UserRepository : IUserRepository
{
    private readonly ILogger _logger;

    // 依賴 ILogger
    public UserRepository(ILogger logger)
    {
        _logger = logger;
    }

    public void Add(string username)
    {
        _logger.Log($"用戶 {username} 已添加到數(shù)據(jù)庫(kù)");
    }
}

// 第三層依賴:用戶服務(wù)(依賴用戶倉(cāng)儲(chǔ))
public class UserService
{
    private readonly IUserRepository _userRepository;

    // 依賴 IUserRepository(其內(nèi)部又依賴 ILogger)
    public UserService(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public void RegisterUser(string username)
    {
        _userRepository.Add(username);
    }
}

// 容器自動(dòng)解析依賴鏈
class Program
{
    static void Main()
    {
        var services = new ServiceCollection();
        // 注冊(cè)所有依賴(只需注冊(cè)抽象與實(shí)現(xiàn)的映射,容器自動(dòng)處理依賴鏈)
        services.AddSingleton<ILogger, ConsoleLogger>();
        services.AddSingleton<IUserRepository, UserRepository>();
        services.AddSingleton<UserService>();

        var provider = services.BuildServiceProvider();
        var userService = provider.GetRequiredService<UserService>();

        userService.RegisterUser("趙六"); 
        // 輸出:[日志] 用戶 趙六 已添加到數(shù)據(jù)庫(kù)(依賴鏈:UserService -> UserRepository -> ConsoleLogger)
    }
}

三、ASP.NETCore 中的依賴注入(實(shí)際應(yīng)用)

在 ASP.NET Core 中,DI 容器已內(nèi)置,通常在 Program.cs 中注冊(cè)服務(wù),在控制器 / 服務(wù)中通過構(gòu)造函數(shù)注入使用。

1. 注冊(cè)服務(wù)(Program.cs)

var builder = WebApplication.CreateBuilder(args);

// 注冊(cè)控制器(默認(rèn)已包含)
builder.Services.AddControllers();

// 注冊(cè)自定義服務(wù)
builder.Services.AddScoped<IMessageSender, EmailSender>(); // 作用域生命周期(適合Web請(qǐng)求)
builder.Services.AddScoped<NotificationService>();

var app = builder.Build();

// 中間件配置...
app.MapControllers();
app.Run();

2. 在控制器中注入服務(wù)

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class NotificationController : ControllerBase
{
    private readonly NotificationService _notificationService;

    // 控制器構(gòu)造函數(shù)注入:容器自動(dòng)傳入 NotificationService(及其依賴)
    public NotificationController(NotificationService notificationService)
    {
        _notificationService = notificationService;
    }

    [HttpGet("{username}")]
    public IActionResult Notify(string username)
    {
        _notificationService.NotifyUser(username);
        return Ok("通知已發(fā)送");
    }
}

總結(jié)

  • 核心方式:構(gòu)造函數(shù)注入是首選,確保依賴不可變且初始化完整。
  • 容器使用:.NET 自帶的 Microsoft.Extensions.DependencyInjection 是主流選擇,通過 “注冊(cè) - 解析” 管理服務(wù),自動(dòng)處理依賴鏈。
  • 生命周期:根據(jù)服務(wù)特性選擇 Transient/Scoped/Singleton,避免因生命周期錯(cuò)誤導(dǎo)致的問題(如在 Singleton 中注入 Scoped 服務(wù))。

以上示例覆蓋了從基礎(chǔ)手動(dòng)注入到框架級(jí)容器使用的場(chǎng)景,可根據(jù)項(xiàng)目規(guī)模選擇合適的方式。

到此這篇關(guān)于C# 中依賴注入(DI)的實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)C# 依賴注入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#實(shí)現(xiàn)仿QQ抽屜式窗體的設(shè)計(jì)方法

    C#實(shí)現(xiàn)仿QQ抽屜式窗體的設(shè)計(jì)方法

    QQ軟件對(duì)于絕大多數(shù)的人來(lái)說再熟悉不過了,它以使用方便、界面美觀及功能完善而著稱,本文給大家介紹了C#實(shí)現(xiàn)仿QQ抽屜式窗體的設(shè)計(jì)方法,主要通過使用API函數(shù)WindowFromPoint和GetParent實(shí)現(xiàn)仿QQ的抽屜式窗體,需要的朋友可以參考下
    2024-04-04
  • 詳解c# 類型轉(zhuǎn)換

    詳解c# 類型轉(zhuǎn)換

    這篇文章主要介紹了c# 類型轉(zhuǎn)換的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以學(xué)習(xí)
    2020-07-07
  • C#中SetStyle的具體使用

    C#中SetStyle的具體使用

    本文主要介紹了C#中SetStyle的具體使用,該方法用于啟用或禁用特定的控件樣式,以控制控件的行為和外觀,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-11-11
  • C#同步、異步遠(yuǎn)程下載文件實(shí)例

    C#同步、異步遠(yuǎn)程下載文件實(shí)例

    使用C#下載一個(gè)Internet上的文件主要是依靠HttpWebRequest/HttpWebResonse和WebClient。具體處理起來(lái)還有同步和異步兩種方式,所以我們其實(shí)有四種組合
    2014-04-04
  • C#交換兩個(gè)變量值的幾種方法總結(jié)

    C#交換兩個(gè)變量值的幾種方法總結(jié)

    這篇文章介紹了C#交換兩個(gè)變量值的幾種方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-10-10
  • vscode編寫latex的方法

    vscode編寫latex的方法

    這篇文章主要介紹了vscode編寫latex的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-07-07
  • C#多線程之線程同步

    C#多線程之線程同步

    這篇文章介紹了C#多線程之線程同步,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • C#中委托的進(jìn)一步理解

    C#中委托的進(jìn)一步理解

    這篇文章主要介紹了C#中委托的進(jìn)一步理解,本文講解了委托類型、建立委托鏈、移除委托鏈等內(nèi)容,需要的朋友可以參考下
    2015-02-02
  • C# 如何生成 DataMatrix 格式的二維碼

    C# 如何生成 DataMatrix 格式的二維碼

    該文主要是利用OnBarcode.dll 生成DataMatrix 格式的二維碼的一些簡(jiǎn)單方法和操作技巧,對(duì)C# 如何生成 DataMatrix 格式的二維碼相關(guān)知識(shí)感興趣的朋友一起看看吧
    2021-11-11
  • Unity Shader實(shí)現(xiàn)裁切效果

    Unity Shader實(shí)現(xiàn)裁切效果

    這篇文章主要為大家詳細(xì)介紹了Unity Shader實(shí)現(xiàn)裁切效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04

最新評(píng)論

闽侯县| 静安区| 安西县| 辉南县| 华池县| 农安县| 扎兰屯市| 松阳县| 巴彦县| 临武县| 时尚| 浪卡子县| 乌拉特中旗| 芜湖县| 蓬莱市| 榕江县| 漠河县| 武陟县| 陈巴尔虎旗| 吉林市| 申扎县| 扬中市| 阆中市| 元氏县| 安龙县| 绥棱县| 讷河市| 汝州市| 安顺市| 广南县| 湛江市| 中方县| 辰溪县| 大渡口区| 庐江县| 开原市| 商水县| 拉孜县| 长武县| 黄冈市| 永丰县|