概述 什麼是反射 概述 什麼是反射 Reflection,中文翻譯為反射。 這是.Net中獲取運行時類型信息的方式,.Net的應用程式由幾個部分:‘程式集(Assembly)’、‘模塊(Module)’、‘類型(class)’組成,而反射提供一種編程的方式,讓程式員可以在程式運行期獲得這幾個組成部分 ...
- 概述 什麼是反射
Reflection,中文翻譯為反射。
這是.Net中獲取運行時類型信息的方式,.Net的應用程式由幾個部分:‘程式集(Assembly)’、‘模塊(Module)’、‘類型(class)’組成,而反射提供一種編程的方式,讓程式員可以在程式運行期獲得這幾個組成部分的相關信息,例如:
Assembly類可以獲得正在運行的裝配件信息,也可以動態的載入裝配件,以及在裝配件中查找類型信息,並創建該類型的實例。
Type類可以獲得對象的類型信息,此信息包含對象的所有要素:方法、構造器、屬性等等,通過Type類可以得到這些要素的信息,並且調用之。
MethodInfo包含方法的信息,通過這個類可以得到方法的名稱、參數、返回值等,並且可以調用之。
諸如此類,還有FieldInfo、EventInfo等等,這些類都包含在System.Reflection命名空間下。
類型 | 作用 |
Assembly | 通過此類可以載入操縱一個程式集,並獲取程式集內部信息 |
EventInfo | 該類保存給定的事件信息 |
FieldInfo | 該類保存給定的欄位信息 |
MethodInfo | 該類保存給定的方法信息 |
MemberInfo | 該類是一個基類,它定義了EventInfo、FieldInfo、MethodInfo、PropertyInfo的多個公用行為 |
Module | 該類可以使你能訪問多個程式集中的給定模塊 |
ParameterInfo | 該類保存給定的參數信息 |
PropertyInfo | 該類保存給定的屬性信息 |
這些都是廢話,我們一起看幾個案列就完全學會了,在此說明下,反射用到的一些基礎技術有 運行運算符,type 類,這裡就不過多的解釋了,如有不會可以去園子裡面自己去找,本人也寫過一篇相關文章,簡單的介紹了運行運算符。
- 如何得到一個類的對象
現有工程文件(項目文件)結構如下
People類代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Entity { public class People { public People() { Console.WriteLine("People被創建了"); } public People(String Name) { this.Name = Name; Console.WriteLine("People被創建了,並且people的名字是"+this.Name); } public string Name { get; set; }//自動屬性,在程式實例化的過程中會自動創建私有的欄位,這個欄位在people 記憶體中開闢控制項存儲其值(本文稱為公有屬性)在此感謝ENC博主的支持和評論, public int Age { get; set; } public string Sex { get; set; } public string msg;//公有欄位 private string qq;//私有欄位 private string address;//私有屬性 public string Address { get => Address; set => Address = value; } public override string ToString() { return "{" + $"name:{this.Name},age:{this.Age},sex{this.Sex}" + "}"; } public string Say() { return "hello! " + this.Name; } } }
debug 目錄如下:
這裡說明下,程式中,並沒有引用 Entity 類庫,也沒有引用Entity..DLL文件,請自行引用,我們如果不實例化得到一個對象呢??正常的時候,我們都是通過new 得到一個對象,如:
using Entity; using System; using System.Collections.Generic; using System.Data; namespace testData { class Program { static void Main(string[] args) { People p = new People(); Console.WriteLine(p); People peop = new People("張三"); Console.WriteLine(p); Console.Read(); } } }
我們再來看下類的類型是什麼?
using Entity; using System; using System.Collections.Generic; using System.Data; namespace testData { class Program { static void Main(string[] args) { Type t = typeof(People); Console.WriteLine(t); Type type= Type.GetType("People"); Console.WriteLine(type);//這裡是得不到的,因為配件裝載只能在程式集內部使用 Console.Read(); } } }
我們來學習下,如何根據類類型進行反射。
- 類的反射
對象無參構造函數反射
static void Main(string[] args) { Type type = typeof(People); People people= Activator.CreateInstance(type) as People;//實例化得帶一個類 Console.WriteLine(people); Console.Read(); }
對象有構造函數參反射
static void Main(string[] args) { Type type = typeof(People); People people= Activator.CreateInstance(type) as People;//實例化得到一個類 Console.WriteLine(people); //實例化得到一個類,該類有一個參數 People p = Activator.CreateInstance(type, new object[] { "Wbcsky" }) as People; Console.WriteLine(p); Console.Read(); }
對象泛型反射
static void Main(string[] args) { Type type = typeof(People); People p1 = Activator.CreateInstance<People>(); Console.WriteLine(p1); Console.Read(); }
關於對象的反射,就只有這三種形式,分別是泛型反射,泛型反射有且只能得到無參數的實例對象,和普通無參反射像比較,反射反射減少了裝箱拆箱的操作。有參數反射我們是按照參數的順序,傳遞的object 數組。這些反射都是基於 Activator.CreateInstance 來完成的。
屬性欄位的反射
static void Main(string[] args) { Type type = typeof(People); System.Reflection.PropertyInfo[] p = type.GetProperties(); foreach (var item in p) { Console.WriteLine("屬性名:" + item.Name + "屬性類型" + item.PropertyType.FullName + "屬性類型命名空間" + item.PropertyType.Namespace); } Console.Read(); }
我們都知道,在C#中,屬性的封裝有兩種,一種全寫,一種簡寫,全寫的在某些工具書中叫做私有屬性,簡寫的在工具書上叫做公有屬性。
如:
public int Age { get; set; }
我們稱為簡寫,工具書上叫做公有屬性。
則:
private string address;//私有屬性
public string Address { get => Address; set => Address = value; }
或
private string iD;
public string ID
{
get { return this.iD; }
set { this.iD = value; }
}
這種寫法我們稱為私有屬性,私有屬性中,當使用=>這種運算的,我們稱為lambda表達式寫法,使用this 關鍵字的寫法,我們稱為面向對象寫法。不論哪一種屬性,我們都叫做屬性,我們在反射中獲取屬性使用的是Type 類的 .GetProperties()方法來獲取類的全部屬性。我們來看下執行結果。
這裡就不過多的介紹獲取屬性的值了,我們在下麵介紹獲取屬性的值。
-
獲取指定名稱的屬性和值及設置一個值
static void Main(string[] args) { Type type = typeof(People); System.Reflection.PropertyInfo Property = type.GetProperty("Name");//註意屬性名稱字元串大小寫 if (Property == null) Console.Read();//如果屬性名稱大小寫錯誤或者不存在,我們Property對象將會是null Console.WriteLine("屬性名:" + Property.Name + "屬性類型" + Property.PropertyType.FullName + "屬性類型命名空間" + Property.PropertyType.Namespace); //獲取屬性的值 People p= Activator.CreateInstance(type) as People;//獲取對象 object oName = Property.GetValue(p); //獲取值 Console.WriteLine("舊" + oName); Property.SetValue(p, "abc");//設置一個值 oName = Property.GetValue(p); //獲取值 Console.WriteLine("新" + oName); Console.Read(); }
看了上面的代碼,我們會發現,獲取屬性使用的是Type類的 GetProperty方法來完成的。獲取值和設置值,使用的是 PropertyInfo 類的 GetValue和Set value 來完成的。執行結果如下
因為初始化的時候是空,所以舊就什麼也沒有輸出。有人會說了,這個沒有獲取到類,進行點寫的方便,為什麼要這麼寫呢,告訴你一句話,存在就是有道理的,這裡可以簡單的告訴,我們很多時候,一個功能更新過於頻繁,我們完全可以把這個類寫入配置文件中,去配置這個類對象的功能使用。理解即可,不理解清背下來代碼。
-
獲取對象的所以公有欄位和私有欄位
在這裡說明下,很多人都不明白欄位和屬性的區別,我這裡簡單說下,理解即可,不理解不影響學習,我們一個類的變數進行封裝,會出現get ,set 設置這個欄位的訪問許可權,這個封裝我們稱為屬性,而這個變數我們叫做欄位,欄位不指定修飾符的時候預設為私有的。
static void Main(string[] args) { Type type = typeof(People); System.Reflection.FieldInfo[] fi = type.GetFields(); Console.WriteLine("\r\n-------------------- 獲取對象的所以公有欄位-------------------------------\r\n"); foreach (System.Reflection.FieldInfo item in fi) { Console.WriteLine("公有欄位名" + item.Name); } Console.WriteLine("\r\n-------------------- 獲取對象的所有私有欄位-------------------------------\r\n"); System.Reflection.FieldInfo[] fiprivate = type.GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); foreach (System.Reflection.FieldInfo item in fiprivate) { Console.WriteLine("私有欄位名" + item.Name); } Console.Read(); }
這是一個難點,但是在實際開發過程中很少使用,但是這我們必須要會,否則後期寫組件開發等文檔,該看不懂了,準備好瓜子,咱們開始聽故事了。
看了上面的代碼,及欄位及屬性的介紹,我們會發現,輸出的結果,共有的很好理解,我們類裡面定義的變數 指定public 以後,我們就可以通過
GetFields ()
方法返回我們想要的公有欄位數組,我們輸出了名字,這裡就不過多的解釋了。
反射私有欄位,輸出的這個是什麼啊,亂碼七招的。
私有欄位名<Name>k__BackingField
私有欄位名<Age>k__BackingField
私有欄位名<Sex>k__BackingField
私有欄位名qq
私有欄位名address
其實很好理解,我們在前面說過獲取所有屬性的時候說過屬性分為私有和公有,其中私有屬性有兩種寫法,其實私有屬性是對私有變數的封裝,也可以說是對私有欄位的封裝,公有屬性是什麼呢?
其實公有屬性在編譯過程中, 為了方便JTL 公共語言運行環境更好的編譯,自動生成了一個私有的欄位,這個欄位是根據操作系統不同生成不同首碼的私有欄位,這裡生成的是K_首碼的。這樣我們就好理解為什麼上圖會多輸出三個欄位。
如果此處還不理解,那麼請看其他博客吧本文介紹的畢竟都是基礎。而實際開發過程中反射這基本使用的都是組件。
- 獲取指定的公有欄位
在這裡就不介紹獲取指定公有欄位的值了,和屬性獲取是一樣的。
static void Main(string[] args) { Type type = typeof(People); Console.WriteLine("\r\n-------------------- 獲取對象的指定公有欄位-------------------------------\r\n"); Console.WriteLine("欄位名" + type.GetField("msg").Name); Console.Read(); }
代碼很簡單,只有一行。那麼有人會問,那欄位分為私有和共有的,為啥沒有介紹獲取私有屬性的呢???為啥沒有介紹獲取指定私有欄位的呢???,其實答案很簡單,你看過有封裝屬性的時候有私有的嗎,私有的是不是都說在類的內部使用,那我反射類就可以了,我外部也不使用。那私有欄位呢,為啥沒有,不是沒有,是有但是基本不使用,因為共有屬性會預設生成私有欄位,這個私有欄位的首碼不同,所以無法獲取,沒意義。所以基本沒人使用。
方法和構造函數的反射
-
獲取公有方法並調用
Type type = typeof(People); Console.WriteLine("\r\n-------------------- 獲取對象的共有方法並且調用-------------------------------\r\n"); System.Reflection.MethodInfo mi = type.GetMethod("Say"); People p= Activator.CreateInstance<People>(); p.Name = "張四夥";//為了省事,這裡不使用屬性反射添加值了 object oReturn = mi.Invoke(p, null);//第一個參數為反射的對象,第二個參數object 數組,為參數,參數按順序填寫 Console.WriteLine(oReturn); Console.Read();
這個沒有什麼解釋的了,前面最難的屬性欄位反射,我們都會了,這個就不是問題了,自己多看看代碼?
-
獲取當前類下的所有夠著函數
static void Main(string[] args) { Type type = typeof(People); ///獲取所有的一般不會使用,這裡就不過多介紹了 System.Reflection.ConstructorInfo[] info = type.GetConstructors();//獲取當前類下所有夠著函數 foreach (System.Reflection.ConstructorInfo item in info) { Console.WriteLine("是否為虛方法"+item.IsVirtual); Console.WriteLine("名稱"+item.Name); } Console.WriteLine("\r\n-------------------- 獲取當前類下參數類型匹配的夠著函數-------------------------------\r\n"); System.Reflection.ConstructorInfo con = type.GetConstructor(new Type[] { typeof(string) }); object o = con.Invoke(new object[] { "zhangsan" }); People peo = o as People; Console.WriteLine(peo); Console.Read(); }
大家會說了,夠著函數不就是類對象的實例化嗎?,我們前面不是講過反射類對象了嗎,為什麼這個裡面還要獲取實例化對象呢?
其實有些時候,我們在使用抽象類和介面的時候,我們通過之前學習的類的反射是一樣可以做到得到類的對象,這裡之說以這麼講解,因為有一些反射項目在優化的時候,會使用內部查找原則,即從夠著函數開始得帶類的對象,效率會更高一些。
我們在開發過程中,儘量有內而外,儘量把計算或者聲明拿到程式代碼執行過程中的最後去做,這樣使用記憶體會少,效率會更高。
下邊我們學習這篇文章的第二大核心。程式集反射
程式集反射
什麼是程式集反射呢,加入我們三層架構,我不想引用bll層和model 層,也不想引用他們的dll,就能在業務層得帶他的對象引用,這個怎麼做到呢???我們一起來學習下吧!
首先程式集中刪除Entity.dll 程式編譯跟目錄放置 ectity.dll文件。看下列代碼
using System; using System.Collections.Generic; using System.Data; namespace testData { class Program { static void Main(string[] args) { /*裝載程式集*/ System.Reflection.Assembly assembly = System.Reflection.Assembly.Load("Entity"); // System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom("Entity.bll");//使用這種方式需要寫擴展名 Console.WriteLine("\r\n-------------------- 程式集反射1-------------------------------\r\n"); Type peopleType = assembly.GetType("Entity.People");//得到people 類的type 類型 object obj = Activator.CreateInstance(peopleType); System.Reflection.MethodInfo me = peopleType.GetMethod("Say"); object ret = me.Invoke(obj, null); Console.WriteLine(ret); Console.WriteLine("\r\n-------------------- 程式集反射2-------------------------------\r\n"); object PeopleObj = assembly.CreateInstance("Entity.People");//直接得到類的實例化對象 Console.WriteLine(PeopleObj); Console.Read(); } } }
代碼註釋已經很明確了,這裡就不過多的解釋了,我們來看下執行結果 。
-------------------- 程式集反射1-------------------------------
People被創建了
hello!
-------------------- 程式集反射2-------------------------------
People被創建了
{name:,age:0,sex}
在程式集反射中,我們就沒有辦法在.屬性 .欄位 .方法的調用了,這個時候,我們只能通過屬性,方法的反射區調用了,這裡演示的不多,就兩種常用的案列,本文只是介紹了開發過程中常用的案列。
總結及擴展
1.反射一般是用在序列化無法完成的情況下,比如介面返回想xml,而這個xml 經常變動,並沒有一個指定的規律,這個時候我們就不能用linq to xml 等反序列化對象了。這個時候就應當使用反射了。
2.真正開發過程中,反射不是是向上面這麼寫的,真正的反射是使用組件來完成的,一般也不會使用程式集反射,除非這個框架的某個功能模塊更新頻繁,我們可以使用不同的反射區完成,只需要在xml 文件中配置下就可以了。
3.在這裡簡單介紹下組件反射,不是說開發過程中不會有程式集等反射,而是大多數的情況下組件反射就已經能滿足我們的需求了,如AutoFac組件,等其他的。
4.反射技術點一般對應的技術點有 IOC 翻轉,依賴倒置,依賴註入等
下邊分享一篇文章,之所以寫本文,就是因為下邊這篇文文章介紹的太主流,很多人不會使用,Autofac是net core 2.0裡面的組件,請看下邊的文章
//格式
//var builder = new ContainerBuilder();
Autofac是一個輕量級的依賴註入的框架,同類型的框架還有Spring.NET,Unity,Castle等。
Autofac的使用有一個非常讓人鬱悶的地方,就是伺服器要求安裝有Microsoft .NET Framework 4 KB2468871。該補丁的地址是:http://www.microsoft.com/zh-cn/download/confirmation.aspx?id=3556 如果不安裝,則運行程式會報如下錯誤:
具體信息可以到這裡去查看:https://code.google.com/p/autofac/wiki/FrequentlyAskedQuestions
這篇文章以一個最簡單的例子,開始Autofac的學習:
namespace ConsoleApplication3{ class Program { static void Main(string[] args) { ContainerBuilder builder = newContainerBuilder(); builder.RegisterType<AutoFacManager>(); builder.RegisterType<Worker>().As<IPerson>(); using (IContainer container = builder.Build()) { AutoFacManager manager = container.Resolve<AutoFacManager>(); manager.Say(); } Console.ReadKey(); } } public interface IPerson { void Say(); } public class Worker : IPerson { public void Say() { Console.WriteLine("我是一個工人!"); } } public class Student : IPerson { public void Say() { Console.WriteLine("我是一個學生!"); } } public class AutoFacManager { IPerson person; public AutoFacManager(IPerson MyPerson) { person = MyPerson; } public void Say() { person.Say(); } }}以上例子,是一個最簡單的例子,輸出如下:
簡單解釋:
1、ContainerBuilder類型的作用
組件通過ContainerBuilder的對象註冊。
2、組件
對象需要從組件中來獲取,比如例子中的Worker類的實例就需要從組件中獲取。
3、哪些實例可以作為組件
- Lambda表達式
- 一個類型
- 一個預編譯的實例
- 實例類型所在的程式集
4、容器
ContainerBuilder的Build()方法可以創建容易,從容器的Resolve()方法能夠獲得對象。
5、為了指定組件服務是某一介面
As()方法將用於註冊時之指定:builder.RegisterTye<TaskController>().As<TController>();
6、組件的依賴關係
組件的依賴關係主要通過介面實現,如Worker:IPerson,
一、組件
創建出來的對象需要從組件中來獲取,組件的創建有如下4種(延續第一篇的Demo,僅僅變動所貼出的代碼)方式:
1、類型創建RegisterType
AutoFac能夠通過反射檢查一個類型,選擇一個合適的構造函數,創造這個對象的實例。主要通過RegisterType<T>() 和 RegisterType(Type) 兩個方法以這種方式建立。
ContainerBuilder使用 As() 方法將Component封裝成了服務使用。
builder.RegisterType<AutoFacManager>(); builder.RegisterType<Worker>().As<IPerson>();2、實例創建
builder.RegisterInstance<AutoFacManager>(new AutoFacManager(new Worker()));單例
提供示例的方式,還有一個功能,就是不影響系統中原有的單例:
builder.RegisterInstance(MySingleton.GetInstance()).ExternallyOwned(); //將自己系統中原有的單例註冊為容器托管的單例這種方法會確保系統中的單例實例最終轉化為由容器托管的單例實例。
3、Lambda表達式創建
Lambda的方式也是Autofac通過反射的方式實現
builder.Register(c => new AutoFacManager(c.Resolve<IPerson>())); builder.RegisterType<Worker>().As<IPerson>();4、程式集創建
程式集的創建主要通過RegisterAssemblyTypes()方法實現,Autofac會自動在程式集中查找匹配的類型用於創建實例。
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()); //在當前正在運行的程式集中找builder.RegisterType<Worker>().As<IPerson>();5、泛型註冊
泛型註冊通過RegisterGeneric() 這個方法實現,在容易中可以創建出泛型的具體對象。
//泛型註冊,可以通過容器返回List<T> 如:List<string>,List<int>等等builder.RegisterGeneric(typeof(List<>)).As(typeof(IList<>)).InstancePerLifetimeScope(); using (IContainer container = builder.Build()) { IList<string> ListString = container.Resolve<IList<string>>(); }6、預設的註冊
如果一個類型被多次註冊,以最後註冊的為準。通過使用PreserveExistingDefaults() 修飾符,可以指定某個註冊為非預設值。
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterType<AutoFacManager>(); builder.RegisterType<Worker>().As<IPerson>(); builder.RegisterType<Student>().As<IPerson>().PreserveExistingDefaults();//指定Student為非預設值 using (IContainer container = builder.Build()) { AutoFacManager manager = container.Resolve<AutoFacManager>(); manager.Say(); //輸出我是一個工人 }如果不使用PreserveExistingDefaults(),那麼將輸出“我是一個學生”。
二、服務
Autofac有三種典型的方式區分服務,同一個服務的不同實現可以由類型,名稱和鍵區分。
1、類型
類型是描述服務的基本方法
builder.RegisterType<Worker>().As<IPerson>(); //IPerson類型的服務和Worker的組件連接起來,這個服務可以創建Worker類的實例並且上面的服務在自動裝備中也有效
AutoFacManager manager = container.Resolve<AutoFacManager>();2、名字
服務可以進一步按名字識別。使用這種方式時,用 Named()註冊方法代替As()以指定名字:
builder.RegisterType<Worker>().Named<IPerson>("worker");使用Name可以檢索服務創建實例:
IPerson p = container.ResolveNamed<IPerson>("worker");ResolveNamed()只是Resolve()的簡單重載,指定名字的服務其實是指定鍵的服務的簡單版本。
3、鍵
有Name的方式很方便,但是值支持字元串,但有時候我們可能需要通過其他類型作鍵。
例如,使用枚舉作為key:
public enum DeviceState { Worker, Student }使用key註冊服務,通過Keyed<T>()方法:
builder.RegisterType<Student>().Keyed<IPerson>(DeviceState.Student);顯式檢索
使用key檢索服務以創建實例,通過ResolveKeyd()方法:
IPerson p = container.ResolveKeyed<IPerson>(DeviceState.Student);ResolveKeyd()會導致容器被當做 Service Locator使用,這是不被推薦的。應該使用IIndex type替代。
IIndex索引
Autofac.Features.Indexed.IIndex<K,V>是Autofac自動實現的一個關聯類型。component可以使用IIndex<K,V>作為參數的構造函數從基於鍵的服務中選擇需要的實現。
builder.RegisterType<Student>().Keyed<IPerson>(DeviceState.Student); using (IContainer container = builder.Build()) {IIndex<DeviceState, IPerson> IIndex = container.Resolve<IIndex<DeviceState, IPerson>>(); IPerson p = IIndex[DeviceState.Student]; p.Say(); //輸出我是一個學生 }IIndex中第一個泛型參數要跟註冊時一致,在例子中是DeviceState枚舉。其他兩種註冊方法沒有這樣的索引查找功能,這也是為什麼設計者推薦Keyed註冊的原因之一。
三、自動裝配
從容器中的可用服務中選擇一個構造函數來創造對象,這個過程叫做自動裝配。這個過程是通過反射實現的,所以實際上容器創造對象的行為比較適合用在配置環境中。
1、選擇構造函數
Autofac預設從容器中選擇參數最多的構造函數。如果想要選擇一個不同的構造函數,就需要在註冊的時候就指定它。
builder.RegisterType(typeof(Worker)).UsingConstructor(typeof(int));這種寫法將指定調用Worker(int)構造函數,如該構造函數不存在則報錯。
2、額外的構造函數參數
有兩種方式可以添加額外的構造函數參數,在註冊的時候和在檢索的時候。在使用自動裝配實例的時候這兩種都會用到。
註冊時添加參數
使用WithParameters()方法在每一次創建對象的時候將組件和參數關聯起來。
List<NamedParameter> ListNamedParameter = new List<NamedParameter>() { new NamedParameter("Id", 1), newNamedParameter("Name", "張三") }; builder.RegisterType<Worker>().WithParameters(ListNamedParameter).As<IPerson>(); 在檢索階段添加參數
在Resolve()的時候提供的參數會覆蓋所有名字相同的參數,在註冊階段提供的參數會覆蓋容器中所有可能的服務。
3、自動裝配
至今為止,自動裝配最大的作用就是減少重覆配置。許多相似的component無論在哪裡註冊,都可以通過掃描使用自動裝配。
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).As<IPerson>();在需要的時候,依然可以創建指定的構造函數創建指定的類。
builder.Register(c => new Worker(2,"關羽"));四、程式集掃描
1、掃描
Autofac可以使用約定在程式集中註冊或者尋找組件。
Autofac可以根據用戶指定的規則在程式集中註冊一系列的類型,這種方法叫做convention-driven registration或者掃描。
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).Where(t => t.Name.EndsWith("Manager"));每個RegisterAssemblyTypes方法只能應用一套規則。如果有多套不同的集合要註冊,那就有必要多次調用RegisterAssemblyTypes。
2、選擇類型
RegisterAssemblyTypes接受程式集的集合。預設情況下,程式集中所有公共具體的類都會被註冊。
如果想要過濾註冊的類型,可以使用Where.向下麵這樣:
Where(t => t.Name.EndsWith("Manager"))如果想要排除某些類型,使用Except():
Except<AutoFacManager)>()或者,自定義那些已經排除的類型的註冊:
Except<Worker>(ct =>ct.As<IPerson>().SingleInstance())多個過濾器可以同時使用,這時他們之間是AND的關係。
3、指定服務
RegisterAssemblyTypes這個註冊方法是註冊單個方法的超集,所以類似As的方法也可以用在程式集中,例如
As<IPerson>();As和Named這兩個方法額外的重載方法接受lambda表達式來決定服務會提供什麼樣的類型。
五、事件
1、激活事件
在component生命周期的不同階段使用事件。
Autofac暴露五個事件介面供實例的按如下順序調用
- OnRegistered
- OnPreparing
- OnActivated
- OnActivating
- OnRelease
這些事件會在註冊的時候被訂閱,或者被附加到IComponentRegistration 的時候。
builder.RegisterType<Worker>().As<IPerson>() .OnRegistered(e => Console.WriteLine("在註冊的時候調用!")) .OnPreparing(e => Console.WriteLine("在準備創建的時候調用!")) .OnActivating(e => Console.WriteLine("在創建之前調用!")) .OnActivated(e => Console.WriteLine("創建之後調用!")) .OnRelease(e => Console.WriteLine("在釋放占用的資源之前調用!"));以上示例輸出如下:
OnActivating
組件被創建之前調用,在這裡你可以:
- 將實例轉向另外一個或者使用代理封裝它
- 進行屬性註入
- 執行其他初始化工作
OnActivated
在component被完全創建的時候調用一次。在這個時候你可以執行程式級別的一些工作(這些工作依賴於對象被完全創建)-這種情況很罕見。
OnRelease
替代component的標準清理方法。實現了IDisposable 介面的標準清理方法(沒有標記為ExternallyOwned) 通過調用Dispose 方法。沒有實現IDisposable或者被標記為ExternallyOwned的清理方法是一個空函數-不執行任何操作。OnRelease 就是用來覆蓋預設的清理行為的。
六、屬性註入
屬性註入使用可寫屬性而不是構造函數參數實現註入。
示例:
builder.Register(c => new AutoFacManager { person = c.Resolve<IPerson>() }); builder.RegisterType<Worker>().As<IPerson>();為了提供迴圈依賴(就是當A使用B的時候B已經初始化),需要使用OnActivated事件介面:
builder.Register(c => new AutoFacManager()).OnActivated(e => e.Instance.person = e.Context.Resolve<IPerson>()); builder.RegisterType<Worker>().As<IPerson>();通過反射,使用PropertiesAutowired()修飾符註入屬性:
builder.RegisterType<AutoFacManager>().PropertiesAutowired(); builder.RegisterType<Worker>().As<IPerson>();如果你預先知道屬性的名字和值,你可以使用:
builder.RegisterType<AutoFacManager>().WithProperty("person", new Worker()); builder.RegisterType<Worker>().As<IPerson>();七、方法註入
可以實現方法註入的方式有兩種。
1、使用Activator
如果你使用委托來激活,只要調用這個方法在激活中
builder.Register(c => { var result = new AutoFacManager(); result.SetDependency(c.Resolve<IPerson>()); return result; }); 註意,使用