從容器中的可用服務中選擇一個構造函數來創造對象,這個過程叫作自動裝配。這個過程是經過反射實現的數組
默認ide
思考這麼一個問題,若是註冊類型中存在多個構造函數,那麼Autofac會選擇哪個來建立類型的實例函數
答案是"儘量最多參數"ui
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 { }static void Main(string[] args)
static void Main(string[] args) { //註冊容器 var builder = new ContainerBuilder(); //向容器中註冊類型 builder.RegisterType<ConstructorClass>(); builder.RegisterType<Class2>(); builder.RegisterType<Class3>(); using (var container = builder.Build()) { #region Resolve對象構造方法選擇原則(當咱們註冊的類型擁有多個構造方法,那麼在Resolve時,將會以儘量最多參數構造方法爲準) var obj = container.Resolve<ConstructorClass>(); Console.WriteLine(obj); #endregion } Console.ReadKey(); }
該實例顯示,選擇的是第三個構造函數,參數爲(Class2 clas2, Class3 clas3),this
按照字面上裏說明」最多參數「,那麼理應執行的是最後一個構造方法或倒數第二個構造方法,可是爲何倒是第三個,這也就是爲何我要加「儘量」三字了。spa
先拋開爲何執行的第三個構造方法,咱們仍是會有疑問」若是執行的是第三個構造方法,那麼Class2和Class3參數分別賦的是什麼值?值又是從哪兒來?「,這裏就涉及到了後面會講到的構造注入。咱們能夠看到,在進行類型註冊時,咱們是對Class2和Class3進行了註冊的,而ConstructorClass又是經過Autofac進行獲取的,因此Class2和Class3參數的值是由Autofac進行初始化賦值的,Class2和Class3沒有自定義構造方法,因此調用的是默認的空構造方法。3d
在知道Class2和Class3參數的初始化與賦值原因後,咱們再來看看以前的那個問題,爲何會執行第三個構造方法,其實如今就好明白了,由於最後兩個的構造方法,一個須要額外的Guid類型參數,另外一個須要Class1類型參數,而這兩個類型又沒有通過註冊,若是調用這兩個構造方法,那麼Auotofac將不知道應該賦何值給這兩個參數,因此Autofac最終選擇了第三個構造方法。code
此時我把第三個構造函數註釋掉以後,會調用第一個構造函數,按照"儘量最多參數"原則,此時不該該調用第二個嗎?答案是,Autofac會在已註冊的類型中尋找,雖然Class2類型被註冊,第二個構造函數Class1類型參數Autofac不知道如何賦值,因此選擇了默認的構造函數,若是在容器中註冊類型Class1取消掉類型Class3的註冊,此時就會調用第二個構造函數.(Autofac尋找構造函數的規則是在已註冊的類型中尋找參數徹底匹配的構造函數)orm
UsingConstructor:指定使用某個構造函數對象
經過上面的例子咱們知道Autofac建立類型實例時會默認從容器中選擇匹配參數最多的構造函數,此時在容器中將Class一、Class二、Class3類型都註冊,此時默認狀況會使用最後一個構造函數,若是若是想要選擇一個不一樣的構造函數,就須要在註冊的時候就指定它,此時指定使用參數爲(Class1 clas1, Class2 clas2)的構造函數
builder.RegisterType<Class1>(); builder.RegisterType<Class2>(); builder.RegisterType<Class3>();
builder.RegisterType<ConstructorClass>().UsingConstructor(typeof(Class1), typeof(Class2));
額外的構造函數參數
有兩種方式能夠添加額外的構造函數參數,在註冊的時候和在檢索的時候。在使用自動裝配實例的時候這兩種都會用到。
註冊時添加參數
使用WithParameters()方法在每一次建立對象的時候將組件和參數關聯起來。
builder.RegisterType<ConstructorClass>().WithParameter("guid", Guid.NewGuid()); //builder.RegisterType<Class1>();//將Class1註冊由於在儘量最多的原則上,出現了兩個最多參數的構造方法,Autofac不知道應該選擇哪一個進行執行 builder.RegisterType<Class2>(); builder.RegisterType<Class3>();
在檢索階段添加參數
在Resolve()的時候提供的參數會覆蓋全部名字相同的參數,在註冊階段提供的參數會覆蓋容器中全部可能的服務。
var obj = container.Resolve<ConstructorClass>(new NamedParameter("guid", Guid.NewGuid()));
自動裝配
在須要的時候,依然能夠建立指定的構造函數建立指定的類。
builder.Register(c => new Clas1());
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; } }