以下为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. StringBuffer与StringBuilder

    有些时候,需要由较短的字符串构建字符串.比如,按键或来自文件中的单词.采用字符串连接的方式达到此目的效率比较低.每次连接字符串的时候,都会构建一个新的String对象,既耗时,又浪费空间.使用Stri ...

  2. PAT1035: Password

    1035. Password (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue To prepare f ...

  3. https://doc.opensuse.org/projects/kiwi/doc/

    KIWI 是用于创建操作系统映像的系统.映像是带有一个文件的目录,该文件包含操作系统.其应用程序与配置.操作系统的文件系统结构.可能的附加元数据,以及(取决于映像类型)磁盘几何属性和分区表数据.通过 ...

  4. 如何使用RedisTemplate访问Redis数据结构

    RedisTemplate介绍 spring封装了RedisTemplate对象来进行对redis的各种操作,它支持所有的 redis 原生的api. RedisTemplate在spring代码中的 ...

  5. PAT1027:Colors In Mars

    1027. Colors in Mars (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue People ...

  6. Socket TCP/UDP

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

  7. java位 、字节 、字符的梳理

    1字节(byte)=8位(bit) char=2字节(这是因为char是Java中的保留字,Java用的是Unicode,所以char在Java中是16位即2个字节的.) 附: String str= ...

  8. String和StringBuffer的区别?

    这个我经常用的是String,说真的,用StringBuffer的次数还真是少,唯一让我觉得特别的方法就是appand这个方法是StringBuffer独有的,那么他们到底有什么区别呢,我们知道Str ...

  9. React Native系列(6) - 编译安卓私有React-Native代码

    为何要自己编译React Native安卓私有代码 我们在开发中遇到一个HTTP2的问题,React Native安卓客户端在和HTTP2支持的服务器通讯的过程中会有crash,见 React-Nat ...

  10. 关于Kafka配额的讨论(2)

    继续前一篇的讨论.前文中提到了两大类配额管理:基于带宽的以及基于CPU线程使用时间的.本文着重探讨基于CPU线程时间的配额管理. 定义 这类配额管理被称为请求配额(request quota),管理起 ...