惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The GitHub Blog
The GitHub Blog
A
About on SuperTechFans
The Cloudflare Blog
G
Google Developers Blog
博客园_首页
Martin Fowler
Martin Fowler
Apple Machine Learning Research
Apple Machine Learning Research
L
LangChain Blog
D
Docker
C
Check Point Blog
T
Tailwind CSS Blog
博客园 - 司徒正美
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
Microsoft Security Blog
Microsoft Security Blog
V
V2EX
博客园 - 叶小钗
T
The Blog of Author Tim Ferriss
酷 壳 – CoolShell
酷 壳 – CoolShell
IT之家
IT之家
M
MIT News - Artificial intelligence
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 【当耐特】
GbyAI
GbyAI

博客园 - 老码识途呀

基于.NET的Windows窗体编程之WinForms自定义控件(1) 基于.NET的Windows窗体编程之WinForms滑块与上下控件 基于.NET的Windows窗体编程之WinForms文件监控 基于.NET的Windows窗体编程之WinForms音频控件 基于.NET的Windows窗体编程之WinForms图像控件 基于.NET的Windows窗体编程之WinForms提示控件 基于.NET的Windows窗体编程之WinForms时间控件 基于.NET的Windows窗体编程之WinForms图表控件 基于.NET的Windows窗体编程之WinForms输入与显示 基于.NET的Windows窗体编程之WinForms菜单工具栏 基于.NET的Windows窗体编程之WinForms打印 基于.NET的Windows窗体编程之WinForms对话框 基于.NET的Windows窗体编程之WinForms列表控件 基于.NET的Windows窗体编程之WinForms数据表格 基于.NET的Windows窗体编程之WinForms事件简介 基于.NET的Windows窗体编程之WinForms布局简介 基于.NET的Windows窗体编程之WinForms控件简介 基于.NET的Windows窗体编程之WinForms入门简介 推荐一款基于.NET和百度飞桨的OCR识别组件 新书速递,手把手教你WPF入门与开发 推荐一种并发线程中资源同步常用方法 推荐一种手动设置异步线程等待机制的解决方案 推荐一种异步线程执行过程中更新进度的方法 推荐一款基于.NET的进程间数据交互经典解决方案 推荐一款线程or进程间数据同步解决方案 推荐一款代码规范检查(CodeReview)神器 推荐一款高性能状态机管理解决方案 推荐一款进程间高速交换数据的解决方案 推荐一款基于.NET的进程间通信框架 推荐一款基于EF-Core的分库分表利器
推荐一款Ioc容器之Ninject入门详解
老码识途呀 · 2025-04-08 · via 博客园 - 老码识途呀

随着软件的不断发展,功能越来越复杂,为了将复杂的逻辑简单化,解耦成了架构师,工程师们经常提起的话题,今天我们就以一个简单的小例子,来看一下解耦的好处,顺便介绍一款Ioc容器Ninject的应用,仅供学习分享使用,如有不足之处,还请指正。

假设场景

在武侠小说中,江湖代表着神秘莫测,身不由己,更有传奇色彩浓郁的江湖英雄榜,剑气纵横,枪出如龙,南帝北丐,东邪西毒,数不胜数。今天我们就以刀剑为例进行说明。

比如,有一个剑的类(Sword),它的主要攻击方式为劈,挑等招数,如下所示:

/// <summary>
/// 剑
/// </summary>
public class Sword
{
    public void Hit(string target)
    {
        Console.WriteLine($"剑将 {target} 劈成两半.");
    }
}

有一个飞镖的类(Shuriken),它的主要攻击方式为远程飞刺,如下所示:

/// <summary>
/// 手里剑,飞镖
/// </summary>
public class Shuriken
{
    public void Hit(string target)
    {
        Console.WriteLine("手里剑刺破 {0} 的盔甲", target);
    }
}

有一位少侠(Samurai),当他用剑时,他的武功招数以劈,挑为主,如下所示:

/// <summary>
/// 武士
/// </summary>
public class Samurai
{
    readonly Sword sword;
    public Samurai()
    {
        this.sword = new Sword();
    }

    /// <summary>
    /// 攻击
    /// </summary>
    /// <param name="target"></param>
    public void Attack(string target)
    {
        this.sword.Hit(target);
    }
}

在上述例子中,剑(Sword)做为兵器的一种,在少侠(Samurai)的构造函数中进行创建,并使用,这样少侠(Samurai)和剑(Sword)就形成了强耦合关系(少侠依赖于剑),假如少侠想使用暗器飞镖(Shuriken)作为武器,又该如何呢?

为了解决这种依赖关系,我们为可以创建接口(interface)来做为剑(Sword)和飞镖(Shuriken)的声明,在这里,我们创建了一个武器接口(IWeapon),它具有攻击的功能(Hit),如下所示:

/// <summary>
/// 武器
/// </summary>
public interface IWeapon
{
    /// <summary>
    /// 攻击
    /// </summary>
    /// <param name="target"></param>
    void Hit(string target);
}

而剑(Sword)和飞镖(Shuriken)分别实现武器接口(IWeapon),如下所示:

剑(Sword)的实现类

public class Sword : IWeapon
{
   public void Hit(string target)
   {
       Console.WriteLine("剑将 {0} 劈成两半", target);
   }
}

飞镖(Shuriken)的实现类

/// <summary>
/// 手里剑,飞镖
/// </summary>
public class Shuriken : IWeapon
{
   public void Hit(string target)
   {
      Console.WriteLine("手里剑刺破 {0} 的盔甲", target);
   }
}

当抽象出来武器接口(IWeapon)后,这样的我们的少侠(Samurai)就可以依赖武器接口(IWeapon),而不是具体的实现剑(Sword)和飞镖(Shuriken),如下所示:

public class Samurai
{
    readonly IWeapon weapon;
    public Samurai(IWeapon weapon)
    {
        this.weapon = weapon;
    }

    public void Attack(string target)
    {
        this.weapon.Hit(target);
    }
}

当少侠(Samurai)想用剑(Sword)时,就传入剑的实现,想用飞镖(Shuriken)时,就传入飞镖的实现,如下所示:

class Program
{
    public static void Main()
    {
        var warrior1 = new Samurai(new Shuriken());//飞镖少侠
        var warrior2 = new Samurai(new Sword());//用剑少侠
        warrior1.Attack("坏人");
        warrior2.Attack("坏人");
    }
}

以上就是面向接口编程的基本实现,只是采用手动的方式进行解耦(需要手动的创建对象),接下来我们将依赖Ioc容器进行自动创建解耦。

Ninject

首先安装Ninject库,在Visual Studio 2022中,可以通过Nuget包管理器进行安装,如下所示:

创建TestModule类,它继承自NinjectModule类,重写Load方法并在Load方法中,通过Bind绑定接口的实现,如下所示:

public class TestModule : NinjectModule
{
    public override void Load()
    {
        Bind<IWeapon>().To<Sword>();
        Bind<Samurai>().ToSelf().InSingletonScope();
    }
}

Ninject提供了一个IKernel接口,以及对接口的标准实现(StandardKernel),在创建IKernel对象时,传递TestModule实例对象,这样就可以获取少侠(Samurai)实例对象,且自动识别依赖关系,并创建注入的IWeapon接口对象,如下所示:

class Program
{
    public static void Main()
    {
        IKernel kernel = new StandardKernel(new TestModule());
        Samurai warrior = kernel.Get<Samurai>();
        warrior.Attack("坏人");
    }
}

在上述示例中,注入的兵器接口(IWeapon)的实现接口为剑(Sword),则少侠(Samurai)自动匹配剑的攻击方式,如下所示:

那么如果少侠(Samurai)在使用剑(Sword)的时候,又想使用飞镖(Shuriken),就是少侠(Samurai)想同时使用两种或多种兵器(IWeapon),这样才能让敌人防不胜防,我们该如何做呢?

接下来,我们修改少侠类(Samurai),将依赖的IWeapon接口改为接口数组,在构造函数中传入IWeapon接口数组,如下所示:

public class Samurai
{
    readonly IWeapon[] weapons;
    public Samurai(IWeapon[] weapons)
    {
        this.weapons = weapons;
    }

    public void Attack(string target)
    {
        foreach (var weapon in weapons)
        {
            weapon.Hit(target);
        }
    }
}

然后在TestModule类中注入兵器(IWeapon)的两个实现剑(Sword)和飞镖(Shuriken),如下所示:

public class TestModule : NinjectModule
{
    public override void Load()
    {
        Bind<IWeapon>().To<Sword>();
        Bind<IWeapon>().To<Shuriken>();
        Bind<Samurai>().ToSelf().InSingletonScope();
    }
}

通过Ioc容器获取少侠(Samurai)实例的方式不变,运行程序,发现少侠(Samurai)在与对手比武时,同时使用了两种功法(Attack),如下所示:

参考文档

关于Ninject的应用还有很多,本文只是简略介绍,更多内容,可参考官方文档:https://github.com/ninject/Ninject/wiki

以上就是《推荐一款Ioc容器之Ninject入门详解》的全部内容,旨在抛砖引玉,一起学习,共同进步