標籤:style blog http io os ar 使用 sp 資料
前言
這本書這幾年零零散散讀過兩三遍了,作為經典書籍,應該重複讀反覆讀,既然我現在開始寫博了,我也準備把以前覺得經典的好書重讀細讀一遍,並且將筆記整理到部落格中,好記性不如爛筆頭,同時也在寫的過程中也可以加深自己理解的深度,當然同時也和技術社區的朋友們共用
程式集載入
- AppDomain.Load
- 盡量避免使用此方法載入程式集,因為Assembly不是從System.MarshalByRefObject派生,所以程式集對象必須按值封送回傳出調用的那個AppDomain,但是CLR會使用發出調用的那個AppDomain的設定來定位並載入程式集。如果搜尋策略和位置沒有找到程式集,會拋出FileNotFoundException異常
- Assembly.Load 推薦的程式集載入方式
- Assembly.LoadFrom 支援從URI載入程式集
- Assembly.LoadFile 載入程式集時,CLR不會自動解析任何依賴性問題;代碼必須向AppDomain的AssemblyResolve事件登記,並讓事件回調方式顯式地載入任何依賴的程式集
反射的缺陷
- 編譯時間無法保證型別安全。嚴重依賴字串,喪失編譯時間的型別安全
- 反射速度慢。載入IO並掃描中繼資料表,字串搜尋,字串搜尋執行的時不區分大小寫比較,這會進一步影響速度
Type類型
- System.Type類型是執行類型和對象操作的起點。是從MemberInfo派生的抽象基類,具體衍生類別包括RuntimeType,ReflectionOnlyType,TypeDelegator,以及System.Reflection.Emit命名空間中的一些類型(包括EnumBuilder,GenericTypeParameterBuilder和TypeBuilder)
- TypeDelegator允許代碼封裝一個Type對象,從而動態產生Type的一個子類,這樣一來,就可以讓原始Type負責處理大部分工作,同時重寫一些需要自訂的功能。這個強大的機制允許重寫反射的工作方式
- 一個類型在AppDomain中被首次訪問時,CLR會構造RuntimeType的一個執行個體,並初始化這個RuntimeType對象的欄位以及Reflect關於類型的資訊
- 實際上Object的GetType非虛執行個體方法,晚期推斷,返回的時RuntimeType對象的一個引用。RuntimeType是FLC內部定義的
- 在一個AppDomain中,每個類型只有一個RuntimeType對象,所以可以使用相等和不等操作符進行判斷
- Type.GetType和重載版本也是一樣返回RuntimeType對象引用
- Type.ReflectionOnlyGetType,類型載入到一個“僅反射”的上下文中,不能執行
- 執行個體方法GetNestedType和GetNestedTypes返回嵌套的子類型
- MakeArrayType(int n) 建立n維數組
- MakeByRefType方法: public void Test(ref Dock d); 通過反射擷取方法參數類型 ParameterType == typeof(Dock).MakeByRefType()
- Type的執行個體屬性包括IsPublic,IsSealed,IsAbstract,IsClass和IsValueType,還有BaseType獲得基底類型等等
- 動態構造類型
- System.Activator.CreateInstance方法,還可以建立派生自MarshalByRefObject的System.Runtime.Remoting.ObjectHandle類型的遠程對象,遠程對象支援跨域傳遞,具體化時會調用ObjectHandle的Unwrap方法,如果對象按引用封送,會建立代理類型的對象。如果對象按值封送,對象的副本會被序列化。此方法還允許在不調用構造器的情況下建立實值型別的一個執行個體
- Activator.CreateInstanceFrom通過字串指定類型及其程式集,程式集使用Assembly.LoadFrom載入到AppDomain中,由於不接受Type參數,返回的都是ObjectHandle對象引用,必須調用ObjectHandle的Unwrap方法進行具體化
- AppDomain的方法,CreateInstance,CreateInstanceAndUnwrap,CreateInstanceFrom,CreateInstanceFromAndwrap
- System.Type的InvokeMember執行個體方法,可使用一個Type對象引用來調用InvokeMember方法,尋找與傳遞的實參匹配的一個構造器,並構造類型
- System.Reflection.ConstructorInfo的Invoke執行個體方法
- 數組、泛型、委託需要特殊的方式進行動態建立
- 建立數組除了MakeArrayType,還有Array的靜態CreateInstance方法。第一個參數都是對數組元素的Type的一個引用
- 建立委託需要調用Delegate的靜態CreateDelegate方法。第一個參數都是對想要建立的委託執行個體的Type的一個引用
- 泛型型別的執行個體,需要使用MakeGenericType執行個體方法,將開放類型變成實際封閉類型
- 注意DeclaringType和ReflectedType屬性,一個是聲明該成員的Type(類聲明就是類,基類聲明就是基類),一個是用於擷取該成員的Type(返回MemberInfo所屬於的那個類型,就是執行反射的類型)
類型成員
- GetMembers擷取所有成員
- 返回特定成員就是GetNestedTypes, GetFields, GetConstructors, GetMethods, GetProperties, GetEvetns方法
- 擷取參數,調用GetParameters方法擷取由ParametereInfo對象構成的一個數組
- 查詢唯讀屬性ReturnParameter獲得ParameterInfo對象獲得成員傳回值的詳細資料
- 泛型型別或方法,調用GetGenericArguments獲得型別參數的集合
- 對於任何一項都可以調用GetCustomAttributes方法獲得應用於它們的自訂attribute的一個集合
- BindingFlags, 篩選返回的成員種類
- BindingFlags.FlattenHierarchy 返回基底類型定義的靜態成員
- 如果指定Public或NonPublic,必須同時指定Instance|Static,否則將不返回成員
- 調用指定成員的方法比如GetMethod或GetMethod,傳遞字串,這是BindingFlags.IgnoreCase標誌就有用了
- 擷取一個類型繼承的介面集合
- 調用Type類型的FindInterfaces(找到介面)
- GetInterface或GetInterfaces方法(擷取介面詳細資料)
- 返回代表介面的Type對象。注意,它掃描類型的繼承階層,並返回在指定類型及其所有的基底類型上定義的所有介面
- 可能針對多個介面定義同一個方法,為了獲得一個特定介面的MethodInfo對象,調用Type的GetInterfaceMap執行個體方法(傳遞介面類型作為實參)。返回System.Reflection.InterfaceMapping(一個實值型別)的一個執行個體(包含了類型和方法的一些定義)
- 對於欄位或屬性,可以擷取值,對於建構函式或方法可以執行call獲得傳回值,PropertyInfo調用call屬性的get和set訪問其方法,EventInfo則可以通過Add或Remove方法添加或刪除一個事件處理常式
- Type類提供InvokeMember方法,可通過它調用一個成員
- InvokeMember內部執行兩個操作。首先,選擇要調用的一個恰當的成員---綁定(bingding)其次,必須實際調用成員---調用(invoking)
- Binder:從System.Reflection.Binder抽象類別型派生的類型。提供BindToField, BindToMethod, ChangeType等方法,Microsoft定義了System.DefaultBinder內部使用
- Type的所有的Get和Set系列的方法,在內部實際上都是調用InvokeMember,只是BindingFlags設定的區別
- 效能的考量,一次綁定,多次調用。將MemberInfo儲存起來
- MemberInfo的執行個體方法都有一個重載版本要擷取對Binder派生對象的引用以及一些BindingFlags,其實不會引起重綁定。調用這些方法時,要在Binder派生對象的協助下,對提供的方法參數進行類型轉換,BindingFlags唯一可以傳遞的標誌時BindingFlags.SuppressChangeType。調用MemberInfo的Invokemethod來綁定並調用一個成員時,SuppressChangeType和ExactBinding可能都要指定,或者都不指定
- Type和MemberInfo派生對象需要大量記憶體儲存。對儲存的效能也會有影響。替代方案就是使用FCL定義的控制代碼類型
- 控制代碼類型:RuntimeTypeHandle, RuntimeFieldHandle, RuntimeMethodHandle。都是實值型別,只包含一個IntPtr的欄位,是一個控制代碼,引用了AppDomain的Loader堆中的一個類型、欄位或方法。簡單、高效的方式將重量級的Type或MemberInfo轉換為輕量級的運行時控制代碼執行個體
- 將Type轉換為RuntimeTypeHandle,調用Type.GetTypeHandle,並傳遞那個Type對象引用,反過來轉換,調用 Type.GetTypeFromHandle,並傳遞那個RuntimeTypeHandle. 其他的派生Memberinfo都有對於的方法轉換為控制代碼
Tips
- 產生程式集或模組時,編譯器建立類型定義表、欄位定義表、方法定義表等其他表,System.Reflection命名空間的類型為程式集或模組中包含的中繼資料建立了一個物件模型
- 應用程式明確式載入程式集,構造類型執行個體,再調用類型中定義的方法。以這種方式綁定並調用方法通常稱為晚期綁定
- 發現程式集中的類型 Assembly.GetExportedTypes
- 晚期綁定使用 typeof(NeedType).IsAssignableFrom(t) 判斷t對象是否相容(實現或派生於)NeedType
- 建議:為了獲得較好的效能和編譯時間的型別安全,盡量避免使用反射。在動態可擴充應用程式下,構造好一個對象只恨,宿主代碼一般要將對象轉型為編譯時間已知的一個介面或者基類。這樣一來,訪問對象的成員時就可以獲得較高的效能,而且可以確保編譯時間的型別安全
後記
不得不說,這本書資訊量真的是很大,以至於做筆記做完了都覺得沒有好好整理和歸納,此筆記僅限於自己記錄和使用,如果少許地方存在紕漏或勘誤的情況,本人概不負責,最終還是要參考書本來學習和理解
讀書筆記—CLR via C#反射