前言

Autofac是一套高效的依赖注入框架。

Autofac官方网站:http://autofac.org/

Autofac在Github上的开源项目:https://github.com/autofac/Autofac

Autofac安装:通过VS的Nuget可以很方便的获取。

 

解析获取方式

Resolve

class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<Class_1>(); //如果注释掉这句,下面Resolve时将会抛出异常 IContainer container = builder.Build();
Class_1 clas1 = container.Resolve<Class_1>();
Console.WriteLine(clas1.Id); Console.Write("Press any key to continue...");
Console.ReadKey();
}
}

class Class_1
{
public Guid Id { get; set; } public Class_1()
{
Id = Guid.NewGuid();
}
}

这种方式在上一篇 Autofac全面解析系列(版本:3.5) – [使用篇(推荐篇):1.类型注册] 的简单示例中有使用到,这种方式在类型已经注册的情况下使用时没问题的,能够获取到注册类型的实例对象,但是如果类型没有经过注册,直接Resolve解析获取,便会抛出异常。

 

ResolveOptional

class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
//builder.RegisterType<Class_1>(); //这里注释掉类型注册的代码 IContainer container = builder.Build();
Class_1 clas1 = container.ResolveOptional<Class_1>();
Console.WriteLine(clas1 == null ? "null" : clas1.Id.ToString()); //这里将会输出null Console.Write("Press any key to continue...");
Console.ReadKey();
}
}

使用Resolve时,如果类型没有经过注册,则会抛出异常,有时我们并不希望程序因此而抛出异常,我们可以使用ResolveOptional进行解析获取,当类型没有经过注册时,ResolveOptional方法将会返回null作为结果。

 

TryResolve

class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
//builder.RegisterType<Class_1>(); //这里注释掉类型注册的代码 IContainer container = builder.Build();
Class_1 clas1 = null;
if (container.TryResolve<Class_1>(out clas1))
{
Console.WriteLine(clas1.Id);
}
else
{//这里将会被执行
Console.WriteLine("null");
} Console.Write("Press any key to continue...");
Console.ReadKey();
}
}

这种方式相信大家并不陌生,这种方式与我们常用的Int32.TryParse相同。使用out参数,并且返回一个bool类型表示是否成功获取到类型实例。

 

其他相关内容

Resolve对象构造方法选择原则

当我们注册的类型拥有多个构造方法,那么在Resolve时,将会以哪个构造方法为准呢?答案是——尽可能最多参数,下面我们以实例来分析:

class ConstructorClass
{
private Class1 _clas1;
private Class2 _clas2;
private Class3 _clas3 = null; public ConstructorClass()
{
_clas1 = null;
_clas2 = new Class2 { Id = -1 };
} public ConstructorClass(Class1 clas1, Class2 clas2)
{
_clas1 = clas1;
_clas2 = clas2;
} public ConstructorClass(Class2 clas2, Class3 clas3)
{
_clas2 = clas2;
_clas3 = clas3;
} public ConstructorClass(Class2 clas2, Class3 clas3, Guid guid)
{
_clas1 = new Class1 {Id = guid};
_clas2 = clas2;
_clas3 = clas3;
} public ConstructorClass(Class1 clas1, Class2 clas2, Class3 clas3)
{
_clas1 = clas1;
_clas2 = clas2;
_clas3 = clas3;
} public override string ToString()
{
return string.Format(
"{{Class1.Id: {0}, Class2.Id: {1}, Class3: {2}}}",
_clas1 == null ? "null" : _clas1.Id.ToString(),
_clas2 == null ? "null" : _clas2.Id.ToString(),
_clas3 == null ? "null" : "not null");
}
}

class Class1
{
public Guid Id { get; set; }
} class Class2
{
public int Id { get; set; }
} class Class3
{ }

//感谢 @就是要说不 的反馈纠正,代码已修正
//主程序
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<ConstructorClass>();
builder.RegisterType<Class2>();
builder.RegisterType<Class3>();
var container = builder.Build();
var obj = container.Resolve<ConstructorClass>();
Console.WriteLine(obj);
Console.Write("Press any key to continue...");
Console.ReadKey();
}
} //构造方法测试类
class ConstructorClass
{
private Class1 _clas1;
private Class2 _clas2;
private Class3 _clas3 = null; public ConstructorClass()
{
_clas1 = null; _clas2 = new Class2 { Id = -1 };
} public ConstructorClass(Class1 clas1, Class2 clas2)
{
_clas1 = clas1; _clas2 = clas2;
} public ConstructorClass(Class2 clas2, Class3 clas3)
{
_clas2 = clas2; _clas3 = clas3;
} public ConstructorClass(Class2 clas2, Class3 clas3, Guid guid)
{
_clas1 = new Class1 { Id = guid }; _clas2 = clas2; _clas3 = clas3;
} public ConstructorClass(Class1 clas1, Class2 clas2, Class3 clas3)
{
_clas1 = clas1; _clas2 = clas2; _clas3 = clas3;
} public override string ToString()
{
return string.Format(
"{{Class1.Id: {0}, Class2.Id: {1}, Class3: {2}}}",
_clas1 == null ? "null" : _clas1.Id.ToString(),
_clas2 == null ? "null" : _clas2.Id.ToString(),
_clas3 == null ? "null" : "not null");
}
} //构造方法参数类型
class Class1
{
public Guid Id { get; set; }
} //构造方法参数类型
class Class2
{
public int Id { get; set; }
} //构造方法参数类型
class Class3
{ }

上面的代码,最终输出结果为 {Class1.Id: null, Class2.Id: 0, Class3: not null} ,最终执行的是第三个构造方法(参数为 Class2, Class3 的)。

根据结果,我们再来理解一下之前说的”尽可能最多参数“,按照字面上里说明”最多参数“,那么理应执行的是最后一个构造方法或倒数第二个构造方法,但是为什么却是第三个,这也就是为什么我要加“尽可能”三字了。

先抛开为什么执行的第三个构造方法,我们还是会有疑问”如果执行的是第三个构造方法,那么Class2和Class3参数分别赋的是什么值?值又是从哪儿来?“,这里就涉及到了后面会讲到的构造注入。我们可以看到,在进行类型注册时,我们是对Class2和Class3进行了注册的,而ConstructorClass又是通过Autofac进行获取的,所以Class2和Class3参数的值是由Autofac进行初始化赋值的,Class2和Class3没有自定义构造方法,所以调用的是默认的空构造方法。

在知道Class2和Class3参数的初始化与赋值缘由后,我们再来看看之前的那个问题,为什么会执行第三个构造方法,其实现在就好明白了,因为最后两个的构造方法,一个需要额外的Guid类型参数,另一个需要Class1类型参数,而这两个类型又没有经过注册,如果调用这两个构造方法,那么Auotofac将不知道应该赋何值给这两个参数,所以Autofac最终选择了第三个构造方法。

这里我们还需要注意一点,如果倒数第二个构造方法的Guid参数给上默认值,那么最后选择的构造方法将会是这个构造方法。让我们再次想想尽可能最多参数这句话就明白了:

public ConstructorClass(Class2 clas2, Class3 clas3, Guid guid = default(Guid))
{
_clas1 = new Class1 { Id = guid };
_clas2 = clas2;
_clas3 = clas3;
}

如果在上面改造了倒数第二个构造方法的基础上继续改造最后一个构造方法,将Class1参数也默认赋值为null,那么最后在Resolve获取ConstructorClass实例时,将会抛出异常。因为在尽可能最多的原则上,出现了两个最多参数的构造方法,Autofac不知道应该选择哪个进行执行。异常信息告诉我们可以使用UsingConstructor来解决这个问题(关于UsingConstructor的用法,将在后续博文中进行说明)。

public ConstructorClass(Class2 clas2, Class3 clas3, Class1 clas1 = null)
{
_clas1 = clas1;
_clas2 = clas2;
_clas3 = clas3;
}

 

解析获取传参

我们在上一节明白了Autofac在Resolve时对构造方法选择的原则,尽可能最多的参数中的参数,可以是已经注册的类型,或是赋给默认值,除了这两种方式,还有一种方式是在Resolve时指定参数。

在上一节中,没有添加默认值的情况下,最后执行的是第三个构造方法。我们可以通过在Resolve时传参来选择更多参数的构造方法:

var obj = container.Resolve<ConstructorClass>(new NamedParameter("guid", Guid.NewGuid()));
//点击查看上一节完整代码

这里仅仅只修改这Resolve的这一句代码,其他代码不变。在Resolve时传入了一个NamedParameter,NamedParameter表示按名字匹配参数,上面的代码表示,为参数名为guid的构造参数传入了Guid.NewGuid值。

这段代码最后执行的是第四个构造方法。因为第四个构造方法是能够匹配的最多参数的构造方法(前两个参数类型已注册到Autofac中,最后一个参数由Resolve时传入的NamedParameter传入)。

Resolve的方法签名为:Resolve<T>(this IComponmentContext context, params Parameter[] parameters)

第一个参数也就是我们使用的container,我们主要关注第二个参数——一个可变的Parameter数组,Parameter是一个抽象类,其中NamedParameter为Parameter的一个子类,除了NamedParameter,还有以下几种子类拱Resolve时使用:

参数类型

参数说明

NamedParameter

根据名称进行匹配

PositionalParameter

根据索引进行匹配,注意:起始索引为0

TypedParameter

根据类型进行匹配,注意:传入多个相同类型的TypedParameter,所有该类型的参数都将采用第一个TypedParameter的值

ResolvedParameter

接收两个Func参数,两个Func签名都接收两个相同的参数ParameterInfo和IComponmentContext,第一个参数为参数的信息(常使用放射的朋友应该熟悉),第二个参数还是当做IContainer使用就好了。第一个Func的返回值为bool,表明当前这个ResolvedParameter是否使用当前匹配到的参数,如果返回true,则会执行第二个Func;第二个Func返回一个object对象,用于填充构造参数值。

 

下面有一个这些Parameter使用的示例供参考:

class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<ParameterClass>(); var container = builder.Build();
container.Resolve<ParameterClass>(
new NamedParameter("value", "namedParameter"), //匹配名字为value的参数
new TypedParameter(typeof (int), 1), //匹配类型为int的参数
new PositionalParameter(4, "positionalParameter"), //匹配第五个参数(注意,索引位置从0开始)
new TypedParameter(typeof (int), -1), //这个将被抛弃,因为前面已经有一个类型为int的TypedParameter
new ResolvedParameter(
//第一个Func参数用于返回参数是否符合要求,这里要求参数是类,且命名空间不是System开头,所以第四个参数将会匹配上
(pi, cc) => pi.ParameterType.IsClass && !pi.ParameterType.Namespace.StartsWith("System"),
//第二个Func参数在第一个Func执行结果为true时执行,用于给参数赋值,也就是第四个参数的值为这个Func的执行结果
(pi, cc) => new Temp {Name = "resolveParameter"})
);
// 最后的输出结果为: {x:1, y:1, value:'namedParameter', temp.Name:'resolveParameter', obj:'positionalParameter'} Console.Write("Press any key to continue...");
Console.ReadKey();
}
} class ParameterClass
{
public ParameterClass(int x, int y, string value, Temp temp, object obj)
{
Console.WriteLine("{{x:{0}, y:{1}, value:'{2}', temp.Name:'{3}', obj:'{4}'}}", x, y, value, temp.Name, obj);
}
} class Temp
{
public string Name { get; set; }
}

class ParameterClass
{
public ParameterClass(int x, int y, string value, Temp temp, object obj)
{
Console.WriteLine("{{x:{0}, y:{1}, value:'{2}', temp.Name:'{3}', obj:'{4}'}}", x, y, value, temp.Name, obj);
}
}

class Temp
{
public string Name { get; set; }
}

 

尾述

本篇博文主要讲述Autofac中比较常用也比较好用的解析获取方式,并没有把所有的解析方式都讲述出来。

本篇博文的重点实际在于理解构造方法的优先原则,解析获取的方式相对简单易用,而获取传参在实际的编码中应用较少,这点在现在还看不出来,因为当前还没有讲到Autofac在编程过程中的实际用法。后续博文将会慢慢道明,请大家继续关注!

Autofac全面解析系列(版本:3.5) – [使用篇(推荐篇):2.解析获取]的更多相关文章

  1. ABP使用及框架解析系列 - [Unit of Work part.1-概念及使用]

    前言 ABP ABP是“ASP.NET Boilerplate Project”的简称. ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开 ...

  2. java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现

    java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...

  3. java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别

    java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别 目录 java基础解析系列(一)---String.StringBuffer.St ...

  4. java基础解析系列(六)---深入注解原理及使用

    java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...

  5. java基础解析系列(七)---ThreadLocal原理分析

    java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...

  6. java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理

    fail-fast机制及CopyOnWriteArrayList的原理 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列( ...

  7. java基础解析系列(九)---String不可变性分析

    java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ...

  8. java基础解析系列(十)---ArrayList和LinkedList源码及使用分析

    java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...

  9. java基础解析系列(十一)---equals、==和hashcode方法

    java基础解析系列(十一)---equals.==和hashcode方法 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系 ...

  10. java基础解析系列(六)---注解原理及使用

    java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer缓存及 ...

随机推荐

  1. Swift 集合类型

     Swift语言提供数组和字典的集合类型  Swift 语言里的数组和字典中存储的数据值类型必须明确 ,即数组中只能存放同类型的数据. 1: 数组 数组的创建 var shoppingList: St ...

  2. javascript 设计模式之观察者模式

    观察者模式又叫发布——订阅模式,顾名思义pub——sub就是被动触发的,:不要给我......,我会给你.......就是一个发布订阅的解释,实质就是对程序中的某个对象状态进行监听观察,并且在该对象发 ...

  3. Adobe flash player更新失败

  4. LeetCode: 221_Maximal Square | 二维0-1矩阵中计算包含1的最大正方形的面积 | Medium

    题目: Given a 2D binary matrix filled with 's and return its area. For example, given the following ma ...

  5. PLSQL Developer 出64位版了

    在win64环境上,一般安装oracle客户端都是64位的了,Toad 也是64位的,但是PLSQL Developer 还是32位的,只能单单为它装一个32位的oracle 客户端,现在退出64位, ...

  6. CentOS7之VMware安装

    选择CentOS的原因 起初也了解了几个linux的发行版,最终决定选择centOS的原因却很简单:阿里云和腾讯云提供的云服务器中centos的版本最多.  VMware12的安装 下载地址:http ...

  7. vbox导入虚拟电脑网卡MAC问题

    vbox导入虚拟电脑之后,需要"重新初始化所有网卡的MAC地址". centos需要做以下几步操作: 1.删除文件"/etc/udev/rules.d/70-persis ...

  8. 使用Service.Stack客户端编写redis pub sub的方法

    pub相对简单 client.PublishMessage("channel", "msg");   sub有2种方法 方法1 var subscription ...

  9. 分享一套精美的现代 UI PSD 工具包【免费下载】

    艾库特·耶尔马兹,是土耳其伊斯坦布尔的一位高级艺术总监,他向大家分享了一套美丽的现代 UI 工具包,你可以免费下载.所以,你可以使用这个优秀的素材设计视觉互动和有吸引力的用户界面. 此 UI 套件提供 ...

  10. SQL查询语句去除重复行

    1.存在两条完全相同的纪录 这是最简单的一种情况,用关键字distinct就可以去掉 select distinct * from table(表名) where (条件) 2.存在部分字段相同的纪录 ...