以下为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. 杨老师课堂_VBA学习教程之根据部门列创建工作表

    课件下载 : 方式1:本节课件下载地址:链接: https://pan.baidu.com/s/1rf5pRmZ95fjVbz70KYi6Aw 密码: q9yk 方式2:或点击此处下载 效果预览图: ...

  2. 设计模式之策略模式(Strategy Pattern)

    模板方法是通过继承实现的,在父类中定义出算法的骨架,将不同点在子类中实现.而策略模式是通过接口实现的,策略中定义了完整的算法.它们有点像啊-- 策略模式的定义 策略模式(Strategy Patter ...

  3. FPA笔记三 数据功能的识别

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://welkinhu.blog.51cto.com/447606/115477     ...

  4. CUDA编程模型之内存管理

    CUDA编程模型假设系统是由一个主机和一个设备组成的,而且各自拥有独立的内存. 主机:CPU及其内存(主机内存),主机内存中的变量名以h_为前缀,主机代码按照ANSI C标准进行编写 设备:GPU及其 ...

  5. 系列博文-LowPoly风格的图像生成器

    https://github.com/Ovilia/Polyvia http://www.zhihu.com/question/29856775/answer/57668656 http://zhan ...

  6. SVN学习之windows下svn的安装

    svn是apache的一个开源项目,全称为subversion.是一个基于版本的项目管理软件,一般在多人开发的项目中使用,目前svn已经替代了原来的cvs.大多数情况下,svn服务安装在linux服务 ...

  7. CentOS 7.2 关闭防火墙

    CentOS7 的防火墙配置跟以前版本有很大区别,CentOS7这个版本的防火墙默认使用的是firewall,与之前的版本使用iptables不一样 1.关闭防火墙: systemctl stop f ...

  8. Visual Studio Code Tips

    新项目要用到Visual Studio Code, 在使用的过程中有些tips, 记录下来以便查阅. 1. 自动保存代码 文件 => 自动保存 2. 帮助输入代码模式 扩展 => 安装HT ...

  9. maven国内镜像(国内oschina的maven服务器关了)

    Maven是官方的库在国外,连下载速度很慢.国内oschina的maven服务器很早之前就关了.今天发现阿里云的一个中央仓库,亲测可用. 找到E:\maven\apache-maven-3.5.2\c ...

  10. hdu 2899 Strange fuction 模拟退火

    求  F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100)的最小值 模拟退火,每次根据温度随机下个状态,再根据温度转移 #include& ...