以下为DI控制反转个人理解烦请各位大牛指教~

编写程序时我们应当遵循抵耦合高内聚的原则(各个功能模块互不依赖).

我们可以利用面向对象里面接口的特性来进行DI控制反转,让功能模块全部依赖接口,而不依赖具体的实现类,当程序跑起来以后通过注入的方式注入具体的实现类如以下代码:

  /// <summary>
/// 购物车类
/// </summary>
public class ShoppingCart {
/// <summary>
/// 创建计算器接口
/// </summary>
IvalueCalculator calculator; /// <summary>
/// 构造函数来注入实际调用的计算方法
/// </summary>
/// <param name="ivalueCalculator"></param>
public ShoppingCart(IvalueCalculator ivalueCalculator)
{
calculator = ivalueCalculator;
} /// <summary>
/// 价格计算
/// </summary>
/// <returns></returns>
public decimal CalculateStockValue()
{
Product[] products = {
new Product {Name = "西瓜", Category = "水果", Price = 2.3M},
new Product {Name = "苹果", Category = "水果", Price = 4.9M},
new Product {Name = "空心菜", Category = "蔬菜", Price = 2.2M},
new Product {Name = "地瓜", Category = "蔬菜", Price = 1.9M}
};
decimal totalValue = calculator.ValueProducts(products);
return totalValue;
} } /// <summary>
/// 计算器实现类
/// </summary>
public class LinqValueCalculator : IvalueCalculator
{
/// <summary>
/// 价格计算实现方法
/// </summary>
/// <param name="products"></param>
/// <returns></returns>
public decimal ValueProducts(params Product[] products)
{
return products.Sum(u => u.Price);
} } /// <summary>
/// 计算器接口
/// </summary>
public interface IvalueCalculator
{
/// <summary>
/// 价格计算方法
/// </summary>
/// <param name="products"></param>
/// <returns></returns>
decimal ValueProducts(params Product[] products);
}

这样,购物车类就实现了松耦合,购物车内的计算价格方法只依赖于计算器接口(IvalueCalculator ),而不依赖具体的计算类(LinqValueCalculator),实际的价格计算类我们通过构造函数的方法注入到购物车内的计算器接口

当我们在实际使用时既可以像如下方法一样实现

static void Main(string[] args)
{
//创建一个接口的对象,引用计算类
IvalueCalculator calculator = new LinqValueCalculator();
//以方法传入具体实现类
ShoppingCart shopping = new ShoppingCart(calculator);
        //调用
Console.WriteLine("价格:{0}", shopping.CalculateStockValue());
Console.ReadLine();
}

可以通过C#的 Ninject 来管理各种注入,只需要提前绑定好接口的对应实现类既可以在使用时去索要一个对应的实现类,如下代码

     //Ninject
IKernel ninjectKernel = new StandardKernel();
//把一个接口(IValueCalculator)绑定到一个实现该接口的类(LinqValueCalculator)
ninjectKernel.Bind<IvalueCalculator>().To<LinqValueCalculator>(); //向NiNject索要一个IvalueCalculator的实现类
IvalueCalculator calcImpl = ninjectKernel.Get<IvalueCalculator>();
//注入购物车类
ShoppingCart shopping = new ShoppingCart(calcImpl); Console.WriteLine("价格:{0}", shopping.CalculateStockValue());
Console.ReadLine();

知识点1:并且,如果你的价格计算实现类(LinqValueCalculator)的内部调用了其它接口,那么Ninject会自动帮你注入要调用接口的实现类(前提是这个调用的接口在之前已经绑定了实现类)

例如:创建一个打折接口(IDiscountHelper),再创建一个类来实现一个默认打折方法(DefaultDiscountHelper)

    /// <summary>
/// 折扣计算接口
/// </summary>
public interface IDiscountHelper {
decimal ApplyDiscount(decimal totalParam);
} /// <summary>
/// 默认折扣计算接口
/// </summary>
public class DefaultDiscountHelper : IDiscountHelper
{
/// <summary>
/// 折扣结算方法
/// </summary>
/// <param name="totalParam"></param>
/// <returns></returns>
public decimal ApplyDiscount(decimal totalParam)
{
return (totalParam - (1m / 10m * totalParam));
}
}

在价格计算实现类(LinqValueCalculator)内调用打折接口(IDiscountHelper)

    /// <summary>
/// 计算器实现类
/// </summary>
public class LinqValueCalculator : IvalueCalculator
{
/// <summary>
/// 定义一个打折接口
/// </summary>
private IDiscountHelper discount; /// <summary>
/// 用构造函数注入打折类
/// </summary>
/// <param name="discountHelper"></param>
public LinqValueCalculator(IDiscountHelper discountHelper) {
discount = discountHelper;
} /// <summary>
/// 价格计算实现方法
/// </summary>
/// <param name="products"></param>
/// <returns></returns>
public decimal ValueProducts(params Product[] products)
{
//调用打折接口的实现类 来计算价格
return discount.ApplyDiscount(products.Sum(u => u.Price));
} }

那么当我们在调用价格计算类(LinqValueCalculator)时,Ninject会自动帮我们注入打折实现类(DefaultDiscountHelper)

(注:前提是,打折接口(IDiscountHelper)你在之前已经用Ninject绑定了它的实现类打折实现类(DefaultDiscountHelper))

 //Ninject
IKernel ninjectKernel = new StandardKernel();
//把一个接口(IValueCalculator)绑定到一个实现该接口的类(LinqValueCalculator)
ninjectKernel.Bind<IvalueCalculator>().To<LinqValueCalculator>(); //给折扣接口绑定一个默认的打折类
ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>(); //向NiNject索要一个IvalueCalculator的实现类
IvalueCalculator calcImpl = ninjectKernel.Get<IvalueCalculator>(); //注意:价格计算类(LinqValueCalculator)类里面有一个折扣接口(IDiscountHelper),折扣接口需要一个具体的折扣类(DefaultDiscountHelper),
//我这里在向 ninjectKernel.Get<IvalueCalculator>() 请求价格计算类(LinqValueCalculator类里面有一个折扣接口)的时候并没有传入折扣实现类
//是ninject自己帮我完成了.
//原理就是在上面我们给打折接口(IDiscountHelper)绑定了一个默认的打折实现类(DefaultDiscountHelper),ninject检测到我们绑定了默认打折实现类(DefaultDiscountHelper),所以自动邦我们补全了; //注入购物车类
ShoppingCart shopping = new ShoppingCart(calcImpl); Console.WriteLine("价格:{0}", shopping.CalculateStockValue());
Console.ReadLine();

知识点2:现在我们的打折实现类(DefaultDiscountHelper)内的折扣力度是写死的,如果我们想让折扣力度由外部传入该肿么办呢?

    /// <summary>
/// 默认折扣计算接口
/// </summary>
public class DefaultDiscountHelper : IDiscountHelper
{
/// <summary>
/// 默认折扣类的打折力度
/// </summary>
public decimal DiscountSize { get; set; } /// <summary>
/// 折扣结算方法
/// </summary>
/// <param name="totalParam"></param>
/// <returns></returns>
public decimal ApplyDiscount(decimal totalParam)
{
return (totalParam - (DiscountSize / 10m * totalParam));
}
}

在NinJect注入具体实现类时,我们可以通过下面的形式对打折力度进去传入

  Ninject核心对象.Bind<绑定的接口>().To<要绑定的实现类>().WithPropertyValue("实现类内部属性的名称",实现类内部属性的值);
        //Ninject
IKernel ninjectKernel = new StandardKernel();
//把一个接口(IValueCalculator)绑定到一个实现该接口的类(LinqValueCalculator)
ninjectKernel.Bind<IvalueCalculator>().To<LinqValueCalculator>(); //给折扣接口绑定一个默认的打折类
ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 5M); //向NiNject索要一个IvalueCalculator的实现类
IvalueCalculator calcImpl = ninjectKernel.Get<IvalueCalculator>(); .......

如果要给多个属性赋值,则可以在Bind和To方式后添加多个WithPropertyValue(<属性名>,<属性值>)方法。

同样的我们也可以以形参的形式传入

    /// <summary>
/// 默认折扣计算接口
/// </summary>
public class DefaultDiscountHelper : IDiscountHelper
{
public DefaultDiscountHelper(decimal discountSize)
{
DiscountSize = discountSize;
} /// <summary>
/// 默认折扣类的打折力度
/// </summary>
public decimal DiscountSize { get; set; } /// <summary>
/// 折扣结算方法
/// </summary>
/// <param name="totalParam"></param>
/// <returns></returns>
public decimal ApplyDiscount(decimal totalParam)
{
return (totalParam - (DiscountSize / 10m * totalParam));
}
}

此时在注入实现类时改为

  Ninject核心对象.Bind<绑定的接口>().To<要绑定的实现类>().WithConstructorArgument("形参的名称",形参的值);

调用方法改为

  //把一个接口(IValueCalculator)绑定到一个实现该接口的类(LinqValueCalculator)
ninjectKernel.Bind<IvalueCalculator>().To<LinqValueCalculator>(); //给折扣接口绑定一个默认的打折类
ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountSize",10M); //向NiNject索要一个IvalueCalculator的实现类
IvalueCalculator calcImpl = ninjectKernel.Get<IvalueCalculator>();

如果构造函数存在多个形参那么在每一个后面在.WithConstructorArgument 即可

例如:

  Ninject核心对象.Bind<绑定的接口>().To<要绑定的实现类>().WithConstructorArgument("形参的名称",形参的值).WithConstructorArgument("形参的名称1",形参的值1).WithConstructorArgument("形参的名称2",形参的值2);

知识点3:在对接口进行绑定时我们都是通过如下方法绑定

  //向NiNject索要一个IvalueCalculator的实现类
IvalueCalculator calcImpl = ninjectKernel.Get<IvalueCalculator>(); //把要到的实现类注入到购物车类内部
ShoppingCart shopping = new ShoppingCart(calcImpl);

转换为中文意为:

由计算(IvalueCalculator) 接口 calcImpl  向  ninjectKernel 索要一个(计算接口)IvalueCalculator的具体实现类(LinqValueCalculator)(具体实现类由上面接口绑定而得)的引用;

创建一个(购物车类)ShoppingCart shopping ,指向(购物车)ShoppingCart类并且由构造函数注入前面从ninjectKernel获得的计算接口(IvalueCalculator)的实现类(LinqValueCalculator);

这样写我个人觉得最通俗易懂,但是不够简洁,Ninject还有另外一种更简单的获取实现类的方法

            ninjectKernel.Bind<IvalueCalculator>().To<LinqValueCalculator>();
//给折扣接口绑定一个默认的打折类
ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>().WithConstructorArgument("discountSize",5M);//标识自我绑定
ninjectKernel.Bind<ShoppingCart>().ToSelf();
//注入购物车类
ShoppingCart shopping = ninjectKernel.Get<ShoppingCart>();

由知识点1可知: Ninject 不仅可以 以  接口 绑定 实现类 的 形式 进行 绑定,也可以进行具体类绑定具体类的操作;

这是自我绑定方法,再由上方知识点可推断  当我们要调用的类需要一个接口的实现类时,如果这个接口是在之前已经进行绑定过的,那么Ninject会自动邦我们进行注入

也就是说:

购物车(ShoppingCart)类先进行绑定,它绑定的实现类就是他自己,所有通过Ninject请求购物车(ShoppingCart)类的请求都会返回一个购物车(ShoppingCart)类自己,再因为 已经在Ninject绑定的接口在被调用时都会自动邦我们进行注入操作

(购物车类内部有一个计算接口(IvalueCalculator)计算接口需要一个计算实现类(LinqValueCalculator)  而计算接口在之前我们已经进行了绑定,所以Ninject会自动帮我们进行注入)

最核心的就是要明白,Ninject会对所有已经绑定过的接口进行自动注入实现类的操作.(貌似有一定的递归逻辑?)

这样整个自绑定逻辑就已经缕清楚了~

由"Liam Wang"编写的"[ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject"整理而成

URL:https://www.cnblogs.com/willick/p/3223042.html

使用Ninject的一般步骤的更多相关文章

  1. [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject

    本人博客已转移至:http://www.exblr.com/liam  为什么需要依赖注入 在[ASP.NET MVC 小牛之路]系列的理解MVC模式文章中,我们提到MVC的一个重要特征是关注点分离( ...

  2. [ASP.NET MVC 小牛之路]05 - 使用 Ninject

    在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来 ...

  3. C#Console程序使用Ninject

    本来想使用一下Ninject的,然后搜索了很久,都没找到比较详细的关于Ninject的使用方法等内容.于是乎干脆自己来写几篇介绍Ninject的内容. 1.      依赖注入和IOC 依赖注入和IO ...

  4. ASP.NET MVC中使用Ninject

    ASP.NET MVC中使用Ninject 在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事 ...

  5. 依赖注入(DI)和Ninject

    [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject 本文目录: 1.为什么需要依赖注入 2.什么是依赖注入 3.使用NuGet安装库 4.使用Ninject的一般步骤 5. ...

  6. [1] Ninject

    为什么使用这种依赖注入的框架呢?我借鉴两张图做一下说明 传统的接口方式,即 IValueCalculator I=new LinqValueCalculator,虽然用接口做的隔离,但是在调用的时候实 ...

  7. 依赖注入框架Ninject

    为什么需要依赖注入 我们提到MVC的一个重要特征是关注点分离(separation of concerns).我们希望应用程序的各部分组件尽可能多的相互独立.尽可能少的相互依赖. 我们的理想情况是:一 ...

  8. 使用 Ninject

    在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来 ...

  9. [ASP.NET MVC 小牛之路]05 - 使用 Ninject实现依赖注入

    在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来 ...

随机推荐

  1. 嵌入Python系列 | 调用Python模块中无参数函数

    开发环境 Python版本:3.6.4 (32-bit) 编辑器:Visual Studio Code C++环境:Visual Studio 2013 需求说明 在用VS2013编写的Win32程序 ...

  2. 基于Microsoft Graph打造自己的Timeline应用

    原文链接:https://github.com/chenxizhang/office365dev/blob/e9b5a59cb827841d36692cc4ec52c11d43062e04/docs/ ...

  3. 定制炫彩界面:duilib与MFC 的对比

    duilib是以DirectUI为技术原理开发的一款轻量级Windows桌面UI库,使用XML来描述界面风格,界面布局,可以很方便的构建高效,绚丽的,非常易于扩展的界面.从而很好的将界面和逻辑分离,同 ...

  4. Socket TCP/UDP

    TCP TCPClient package com.tcp; import java.io.*; import java.net.*; class TCPClient { public static ...

  5. Linux kernel的中断子系统之(五):驱动申请中断API

    返回目录:<ARM-Linux中断系统>. 总结:二重点区分了抢占式内核和非抢占式内核的区别:抢占式内核可以在内核空间进行抢占,通过对中断处理进行线程化可以提高Linux内核实时性. 三介 ...

  6. elasticsearch 的安装配置与spring boot的整合应用

    linux上的elasticsearch安装 一.下载elasticsearch 直接进入elasticsearch的官网,下载最新的安装包:https://www.elastic.co/downlo ...

  7. PyCharm 专题

    pycharm常用设置 pycharm中的设置是可以导入和导出的,file>export settings可以保存当前pycharm中的设置为jar文件,重装时可以直接import settin ...

  8. 当Ucenter和应用通信失败

    http://blog.sina.com.cn/s/blog_775f158f010135uz.html 失败是常见的. 对于初次接触Ucenter的人来讲,添加一个自己的应用最头疼的就是发现通信失败 ...

  9. poj-3522 最小生成树

    Description Given an undirected weighted graph G, you should find one of spanning trees specified as ...

  10. centos7安装libgdiplus。netcore生成验证码,处理图片

    yum install autoconf automake libtool yum install freetype-devel fontconfig libXft-devel yum install ...