在 Ioc 世界中,有些框架(例如 Autofac/NInject/Unity)支持传递默认参数,有些框架(例如 SimpleInjector/LightInjector 等)则不支持。作为 My.Ioc 来说,我们支持默认参数。

当我们在 My.Ioc 中注册对象时,有些对象类型 (System.Type) 要求我们必须提供默认参数,而有些则不是必要的。在 My.Ioc 中,默认参数有两个作用:1. 容器根据默认参数来选择用于构建对象的构造函数。而一旦选定构造函数之后,今后容器便会一直使用该构造函数来构造对象实例;2. 如果我们在向容器请求对象实例(即调用任何一个 container.Resolve 重载方法)时没有传入覆盖参数(即未调用任何包含 overridenParameters 参数的 container.Resolve 重载方法),容器便会使用这些默认参数来构造对象实例。下面我们通过一个示例来加以说明:

using System;
using System.Diagnostics;
using My.Ioc;namespace ConstructorSelectAndParameterOverride
{#region Test Typespublic class ParameterClass1{}public class ParameterClass2{}public class ParameterClass3{}public class ParameterClass4{}public class ParameterClass5{}public class PositionalTarget{private ParameterClass1 _c1;private ParameterClass2 _c2;private ParameterClass3 _c3;private ParameterClass4 _c4;private ParameterClass5 _c5;private int _age;private string _name;public PositionalTarget(ParameterClass1 c1, int age, string name, ParameterClass2 c2, ParameterClass3 c3, ParameterClass4 c4){_c1 = c1;_c2 = c2;_c3 = c3;_c4 = c4;_name = name;_age = age;}public PositionalTarget(ParameterClass1 c1, int age, string name, ParameterClass5 c5, ParameterClass3 c3, ParameterClass4 c4){_c1 = c1;_c5 = c5;_c3 = c3;_c4 = c4;_name = name;_age = age;}public ParameterClass5 ParameterClass5{get { return _c5; }}public ParameterClass2 ParameterClass2{get { return _c2; }}public string Name{get { return _name; }}public int Age{get { return _age; }}}public class NamedTarget{private ParameterClass1 _c1;private ParameterClass2 _c2;private ParameterClass3 _c3;private ParameterClass4 _c4;private ParameterClass5 _c5;private int _age;private string _name;public NamedTarget(ParameterClass1 c1, int age, ParameterClass2 c2, ParameterClass3 c3, string name, ParameterClass4 c4){_c1 = c1;_c2 = c2;_c3 = c3;_c4 = c4;_name = name;_age = age;}public NamedTarget(ParameterClass1 c1, int age, ParameterClass5 c5, ParameterClass3 c3, ParameterClass4 c4){_c1 = c1;_c5 = c5;_c3 = c3;_c4 = c4;_age = age;}public ParameterClass5 ParameterClass5{get { return _c5; }}public ParameterClass2 ParameterClass2{get { return _c2; }}public string Name{get { return _name; }}public int Age{get { return _age; }}}#endregionclass Program{static void Main(string[] args){IObjectContainer container = new ObjectContainer(false);Register(container);ResolvePositionalTargetWithoutOverridingParameters(container);ResolvePositionalTargetAndOverrideWithPositionalParameters(container);ResolvePositionalTargetAndOverrideWithNamedParameters(container);ResolveNamedTargetWithoutOverridingParameters(container);ResolveNamedTargetAndOverrideWithPositionalParameters(container);ResolveNamedTargetAndOverrideWithNamedParameters(container);Console.ReadLine();}static void Register(IObjectContainer container){container.Register<ParameterClass1>();container.Register<ParameterClass2>();container.Register<ParameterClass3>();container.Register<ParameterClass4>();container.Register<ParameterClass5>();container.Register<PositionalTarget>().WithConstructor(Parameter.Auto,Parameter.Positional(30),Parameter.Positional("China"),Parameter.Positional<ParameterClass5>());container.Register<NamedTarget>().WithConstructor(Parameter.Named("age", 90));container.CommitRegistrations();}static void ResolvePositionalTargetWithoutOverridingParameters(IObjectContainer container){Console.WriteLine("Resolve PositionalTarget without overriding default parameters: ");var positional = container.Resolve<PositionalTarget>();Debug.Assert(positional.ParameterClass5 != null);Console.WriteLine(positional.Name);Console.WriteLine(positional.Age);}static void ResolvePositionalTargetAndOverrideWithPositionalParameters(IObjectContainer container){Console.WriteLine("Resolve PositionalTarget and override default parameters using positional parameters: ");var positional = container.Resolve<PositionalTarget>(Parameter.Auto,Parameter.Positional(99),Parameter.Positional("ZhongHua"));Debug.Assert(positional.ParameterClass5 != null);Console.WriteLine(positional.Name);Console.WriteLine(positional.Age);}static void ResolvePositionalTargetAndOverrideWithNamedParameters(IObjectContainer container){Console.WriteLine("Resolve PositionalTarget and override default parameters using named parameters: ");var positional = container.Resolve<PositionalTarget>(Parameter.Named("name", "HuaXia"));Debug.Assert(positional.ParameterClass5 != null);Console.WriteLine(positional.Name);Console.WriteLine(positional.Age);}static void ResolveNamedTargetWithoutOverridingParameters(IObjectContainer container){Console.WriteLine("Resolve NamedTarget without overriding default parameters: ");var named = container.Resolve<NamedTarget>();Debug.Assert(named.ParameterClass5 != null);Debug.Assert(named.Name == null);Console.WriteLine(named.Age);}static void ResolveNamedTargetAndOverrideWithPositionalParameters(IObjectContainer container){Console.WriteLine("Resolve NamedTarget and override default parameters using positional parameters: ");var named = container.Resolve<NamedTarget>(Parameter.Auto,Parameter.Positional(12));Debug.Assert(named.ParameterClass5 != null);Debug.Assert(named.Name == null);Console.WriteLine(named.Age);}static void ResolveNamedTargetAndOverrideWithNamedParameters(IObjectContainer container){Console.WriteLine("Resolve NamedTarget and override default parameters using named parameters: ");var named = container.Resolve<NamedTarget>(Parameter.Named("age", 68));Debug.Assert(named.ParameterClass5 != null);Debug.Assert(named.Name == null);Console.WriteLine(named.Age);}}
}

View Code

在上面这个示例中,我们看到有两个类 PositionalTarget 和 NamedTarget,这两个是我们打算向容器请求的目标类。之所以有两个,是因为我们要分别演示两种传递默认参数的方法,即命名参数 (Named Parameter) 和定位参数 (Positional Parameter) 的用法。

在 My.Ioc 中,我们把参数分为两个大类:一种是可以自动装配 (Autowirable) 的参数,一种是不可自动装配 (NonAutowirable) 的参数。不可自动装配的参数,顾名思义,容器没有办法为这类参数自动构建一个参数值。这类参数包括所有值类型,以及 string 型和 Type 型(请参见 My.Ioc.Helpers.TypeExtensions 的 IsNotAutowirable 和 IsAutowirable 两个扩展方法)。可以自动装配的参数则是除了不可自动装配的参数之外的所有其他参数类型,这种类型的参数是容器可以自动装配的(废话),它们要么是已经注册的类型,要么是符合某种约定的、容器可以自动注册/解析的类型(参见My.Ioc 代码示例——实现自动注册/解析)。

对于可以自动装配的参数来说,是否提供默认值是可选的。而对于不可自动装配的参数来说,由于其是不可自动装配的(又是废话),因此我们必须为其提供一个默认值。

如果要提供默认参数值,则有两个选择:或是以定位参数方式提供默认值,或是以命名参数方式提供默认值,二者不可混用。也就是说,在提供默认参数值时,不能同时使用命名参数和定位参数,只能任意选用其中一种。譬如,下面这种用法就是错误的(在 IDE 中也会提示出错,而无法通过编译):

container.Register<PositionalTarget>().WithConstructor(Parameter.Auto,Parameter.Positional(30),Parameter.Named("name", "China"),Parameter.Positional<ParameterClass5>());

在选择(或者说匹配)构造函数时,无论使用定位参数还是命名参数,有一个原则是二者都必须共同遵循的,那就是 [所有“不可自动装配的参数”都必须显式指定一个默认值]。但在用法方面,二者略有一些不同。

使用定位参数时,容器将根据参数位置来匹配参数。这里有一点要特别说明一下。在一些构造函数中,有可能会出现“不可自动装配的参数”位于“可以自动装配的参数”后面的情况(本文的示例代码中给出的就是这种情况)。在这种情况下,由于是按位置匹配参数,因此对于排在“不可自动装配的参数”之前的“可以自动装配的参数”,也需要显式提供一个默认值。但此时我们既可以指定提供一个该类型的对象实例作为参数值,也可以使用“Parameter.Auto”。后者是 My.Ioc 容器中提供的一种占位参数,表示此构造参数的参数值将由容器自行决定(即由容器自动装配)。

使用命名参数时,参数位置不重要,容器将会根据参数名称来匹配参数。一般说来,只要参数名称正确,而且为所有“不可自动装配的参数”都提供了默认值,容器便能成功匹配构造函数。

有了上面这些知识作为背景,接下来我们来看一下 PositionalTarget 这个类的构造函数。我们看到在 PositionalTarget 这个类中,有两个构造函数,其签名分别是:

public PositionalTarget(ParameterClass1 c1, int age, string name, ParameterClass2 c2, ParameterClass3 c3, ParameterClass4 c4)
public PositionalTarget(ParameterClass1 c1, int age, string name, ParameterClass5 c5, ParameterClass3 c3, ParameterClass4 c4)

在这两个构造函数中,都包含了 int 类型和 string 类型的构造参数,因此,我们知道要想构造这个类的对象,无论选择哪个构造函数都是需要提供默认参数的。

在示例代码中,对象注册是在 Register 方法中完成的。因此,接下来我们就来看一下这个 Register 方法:

 1 static void Register(IObjectContainer container)
 2 {
 3     container.Register<ParameterClass1>();
 4     container.Register<ParameterClass2>();
 5     container.Register<ParameterClass3>();
 6     container.Register<ParameterClass4>();
 7     container.Register<ParameterClass5>();
 8
 9     container.Register<PositionalTarget>()
10         .WithConstructor(
11         Parameter.Auto,
12         Parameter.Positional(30),
13         Parameter.Positional("China"),
14         Parameter.Positional<ParameterClass5>());
15
16     container.Register<NamedTarget>()
17         .WithConstructor(Parameter.Named("age", 90));
18
19     container.CommitRegistrations();
20 }

在这个方法中,我们首先注册了 ParameterClass1、ParameterClass2、ParameterClass3、ParameterClass4 和 ParameterClass5 等类,这几个类型将要用作 PositionalTarget 和 NamedTarget 的构造参数,它们也是所谓的“可以自动装配的参数”。由于它们已经注册到容器中,因此容器在创建 PositionalTarget 和 NamedTarget 的实例时,便可以自动创建这几个类的实例以满足构造函数需要(也即自动装配的意思)。

接下来,在第 9 行到第 14 行中,我们采用定位参数的方式指定了用于构造 PositionalTarget 对象的默认参数。我们看到第一个默认参数是 Parameter.Auto。这是由于该参数是 ParameterClass1 类型(属于“可以自动装配的参数”),但它后面的构造参数是 int 类型(属于“不可自动装配的参数”),所以我们这里需要为它提供一个默认参数值。

后面两个参数 Parameter.Positional(30) 和 Parameter.Positional("China") 的含义都比较简单,这里略过不谈。

我们看到最后还提供了一个默认参数 Parameter.Positional<ParameterClass5>()。这里之所以要提供这个参数,是因为 PositionalTarget 这个类的构造函数中,前面几个构造参数的参数类型都完全相同,因此没有办法区分到底匹配哪个构造函数。只有到了第四个构造参数时,其参数类型才显示出有所不同(一个是 ParameterClass5 类型,一个是 ParameterClass2 类型),因此我们这里需要提供这个默认参数才能加以区分。当我们提供了这个默认参数之后,要选用哪个构造函数的问题也就一目了然了。

在 Register 这个方法的末后,可以看到我们注册了 NamedTarget 这个类型。NamedTarget 这个类型也有两个构造函数,分别是:

public NamedTarget(ParameterClass1 c1, int age, ParameterClass2 c2, ParameterClass3 c3, string name, ParameterClass4 c4)
public NamedTarget(ParameterClass1 c1, int age, ParameterClass5 c5, ParameterClass3 c3, ParameterClass4 c4)

在上面注册 NamedTarget 的代码中,我们看到只提供了一个命名参数 Parameter.Named("age", 90)。根据 [所有“不可自动装配的参数”都要显式指定一个默认值] 的原则,我们也可以很容易明白作者想要选用的是下面一个构造函数。

上面,我们向大家介绍了在什么情况下需要提供默认参数以及如何提供默认参数。接下来,我们将向各位简单介绍一下如何覆盖在注册时提供的默认参数。

在一般的编程实践中,如果我们要创建一个对象,我们可以给这个对象的构造函数传递不同的参数值以创建不同的对象实例。My.Ioc 中的覆盖参数 (Overridden Parameters) 的意义也即在此。通过使用覆盖参数,我们也可以要求容器为我们提供不同的对象实例。

在使用覆盖参数时,由于此时对象的默认构造函数已经由容器选定,而且所有不可自动装配的参数也都有了默认值,因此我们不需要为所有“不可自动装配的参数”指定一个覆盖参数值。如果我们想要覆盖某个默认参数,我们便为其提供一个覆盖参数值;如果我们不需要覆盖某个默认参数,只需略过该参数即可。但是,我们传递的覆盖参数仍然要符合一定的原则。

譬如,如果我们以定位方式传递覆盖参数,仍然需要符合 [对于排在“不可自动装配的参数”之前的“可以自动装配的参数”,需要显式提供一个覆盖参数值] 这个原则。也就是说,即使我们要覆盖的目标参数前面的参数是可以自动装配的,我们也要为其提供一个覆盖参数值。一般情况下,提供 Parameter.Auto 即可。此外,如果在要覆盖的目标参数的前面还有其他“不可自动装配的参数”,而我们并不想覆盖它们,也可以使用 Parameter.Auto。

使用命名参数方式提供覆盖参数时,只需按名称指定要覆盖的参数,并为其提供一个参数值即可。用法比较简单,此处不再多言。

本文示例代码以及 My.Ioc 框架源码可在此处获取。

转载于:https://www.cnblogs.com/johnny-liu/p/3962824.html

My.Ioc 代码示例——如何使用默认构造参数,以及如何覆盖默认构造参数相关推荐

  1. My.Ioc 代码示例——避免循环依赖

    本文的目的在于通过一些示例,向大家说明 My.Ioc 支持哪些类型的依赖关系.也就是说,如何设计对象不会导致循环依赖. 在 Ioc 世界中,循环依赖是一个顽敌.这不仅因为它会导致 Ioc 容器抛出异常 ...

  2. python中的figure什么意思_Python Matplotlib.figure.Figure.text()用法及代码示例

    Matplotlib是Python中的一个库,它是数字的-NumPy库的数学扩展. Figure模块提供了顶层Artist,即Figure,其中包含所有绘图元素.此模块用于控制所有图元的子图和顶层容器 ...

  3. Android车载系统(HVAC) 原理+源码分析(代码示例)

    前言 Android车载系统(HVAC)是一种用于控制车内温度.空气质量和湿度的系统,它是一项重要的汽车电子技术.在这种系统中,CarHvacManager控制器是一个重要的组件,它通过与车载传感器和 ...

  4. 【Groovy】闭包 Closure ( 闭包调用 | 闭包默认参数 it | 代码示例 )

    文章目录 一.调用闭包 二.闭包默认参数 it 三.代码示例 一.调用闭包 执行 Closure 变量 的 call() 方法 , 可以调用该闭包 ; // 定义闭包变量def closure = { ...

  5. 【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入代码示例 )

    文章目录 总结 一.Android 事件依赖注入示例 1.创建依赖注入库 2.声明注解 (1).修饰注解的注解 (2).修饰方法的注解 3.Activity 基类 4.动态代理类调用处理程序 5.依赖 ...

  6. 【IOC 控制反转】Android 视图依赖注入 ( 视图依赖注入步骤 | 视图依赖注入代码示例 )

    文章目录 总结 一.Android 视图依赖注入步骤 二.Android 布局依赖注入示例 1.创建依赖注入库 2.声明注解 3.Activity 基类 4.依赖注入工具类 5.客户端 Activit ...

  7. 标签select选择框通过js来选择默认值;代码示例

    页面代码: <select data-am-selected id="table-item-capital-source" name="capital_source ...

  8. 用户自定义协议client/server代码示例

    用户自定义协议client/server代码示例 代码参考链接:https://github.com/sogou/workflow message.h message.cc server.cc cli ...

  9. 【Android 内存优化】自定义组件长图组件 ( 长图滚动区域解码 | 手势识别 GestureDetector | 滑动计算类 Scroller | 代码示例 )

    文章目录 一.GestureDetector 创建与设置 二.GestureDetector 触摸事件传递 三.触摸滑动操作 四.惯性滑动操作 五.长图滑动组件代码示例 六.运行效果 七.源码及资源下 ...

  10. HDFS的API调用,创建Maven工程,创建一个非Maven工程,HDFS客户端操作数据代码示例,文件方式操作和流式操作

    1. HDFS的java操作 hdfs在生产应用中主要是客户端的开发,其核心步骤是从hdfs提供的api中构造一个HDFS的访问客户端对象,然后通过该客户端对象操作(增删改查)HDFS上的文件 1.1 ...

最新文章

  1. 22岁复旦大学生拿下深度学习挑战赛冠军:明明可以靠脸吃饭,却偏偏要靠才华
  2. 利用Python如何实现数据驱动的接口自动化测试
  3. 华三服务器怎么设置系统启动模式,H3C 开局设置
  4. [SAP成都] SAP UI5应用的sap-ui-core.js被加载之前,还有哪些js文件被加载了
  5. java面试手写单链表_(转)面试大总结之一:Java搞定面试中的链表题目
  6. 中如何刷新当前路由_企业装修和家庭改造中,路由器及接收器如何安放
  7. python中加等是什么意思_Python创建类的时候,加()和不加有什么区别、联系?
  8. 支付361万元“家庭抚养费”后,贾跃亭向法院申请与甘薇离婚?
  9. Android View移动的六种方法
  10. 21. 总是让比较函数在等值情况下返回false
  11. Apache Solr 全文检索概述 与 Win10 下安装 Solr
  12. webview加载百度失败_独家|神秘SDK暗刷百度广告 植入数千款APP
  13. linux系统 ghost,Linux下用GHOST来做系统备份
  14. QT designer 控件自适应
  15. vpay平台模式开发 15天交付系统
  16. 【处理办法】USB插入TF卡或SD卡,计算机提示需格式化
  17. 慌乱的表情,泄露了我的悲伤:伤感心情日志
  18. 计算机实验三——Rdt协议对比
  19. typhon例子:cs,bufDataset(类似delphi的clientDataset),josn解析器
  20. 名医高效良方(三叉神经痛)

热门文章

  1. 来自18年9月底的总结及展望
  2. spring实现定时任务的两种方式
  3. SQL Server将DataTable传入存储过程(Table Value Parameter)
  4. Url...................哈哈哈哈哈哈哈哈哈
  5. 项目开发经常使用PHP功能
  6. Codeforces Round #288 (Div. 2)E. Arthur and Brackets
  7. xshell用ssh连接VMware中的ubuntu
  8. Nutch1.2二次开发详细攻略(一)【图文】------Windows平台下Cygwin环境的搭建
  9. 独家揭秘:用友云的新打算
  10. 使用线程池管理线程!