Reflection
-
-
- 1.反射
- 1.1 System.Type類
- 1.2 使用System.Object.GetType()得到Type的引用
- 1.3 使用typeof()得到Type引用
- 1.4 使用System.Type.GetType()得到Type引用
- 2.構(gòu)建自定義的元數(shù)據(jù)查看器
- 2.1 反射方法
- 2.2 反射字段和屬性
- 2.3 反射實現(xiàn)的接口
- 2.4 顯示其他信息
- 2.5 反射泛型信息
- 2.6 反射方法參數(shù)和返回值
- 3.動態(tài)加載程序集
- 4.反射共享程序集
- 5.晚期綁定
- 6.使用早期綁定反射特性
- 7.使用晚期綁定反射特性
- 8.反射、晚期綁定使用背景
- 9.反射的性能
- 1.反射
-
1.反射
在.Net中,反射(reflection)是一個運行庫類型發(fā)現(xiàn)的過程。通過反射可以得到一個給定.dll或.exe程序集所包含的所有類型的列表,其中包括類型定義的方法、字段、屬性和事件,也可以動態(tài)發(fā)現(xiàn)一組給定類型支持的接口、方法的參數(shù)或其他相關(guān)細節(jié)(基類、命名空間、清單數(shù)據(jù)等)。
System.Reflection命名空間成員示例:
| Assembly | 該抽象類包含了許多靜態(tài)方法,通過它可以加載、了解和操縱一個程序集 |
| AssemblyName | 使用該類可以找到大量隱藏在程序集的身份中的細節(jié)(版本信息、區(qū)域信息等) |
| EventInfo | 該抽象類保存給定事件的信息 |
| FieldInfo | 該抽象類保存給定字段的信息 |
| MemberInfo | 該類是抽象類,它為EventInfo、FieldInfo、MethodInfo和PropertyInfo類型定義了公共的行為 |
| MethodInfo | 該抽象類保存給定方法的信息 |
| Module | 該抽象類可以訪問多文件程序集中的給定模塊 |
| ParameterInfo | 該類保存給定參數(shù)的信息 |
| PropertyInfo | 該抽象類保存給定屬性的信息 |
1.1 System.Type類
System.Type的部分成員:
| IsAbstract、Isarray、IsClass、IsCOMObject、IEnum、 IsGenericTypeDefinition、IsGenericParameter、 IsInterface、IsPrimitive、IsNestedPrivate、 IsNestedPublic、IsSealed、IsValueType | 用于發(fā)現(xiàn)所引用類型的基本特性 |
| GetConstructors()、GetEvents()、GetFields()、 GetInterfaces()、GetMembers()、GetMethods()、 GetNestedType()、GetProperties() | 得到接口、方法、屬性等的數(shù)組(每個方法都有單數(shù)版本,如GetMethod()) |
| FindMembers() | 該方法根據(jù)查詢條件返回一個MemberInfo類型的數(shù)組 |
| GetType() | 該靜態(tài)方法返回一個Type實例,給定一個字符串名稱 |
| InvokeMember | 該方法允許對給定項目的晚期綁定 |
1.2 使用System.Object.GetType()得到Type的引用
使用System.Object定義的GetType()方法,返回一個表示當(dāng)前對象元數(shù)據(jù)的Type類的實例。
SportsCar sc = new SportsCar(); Type t = sc.GetType(); // 需要創(chuàng)建實例,需知道類型的編譯時信息1.3 使用typeof()得到Type引用
另一種獲取類型信息的方法是使用C# typeof操作符。
Type t = typeof(SportsCar); // 無需創(chuàng)建實例,需知道類型的編譯時信息1.4 使用System.Type.GetType()得到Type引用
使用System.Type.GetType()得到Type引用,不需要得到正從中提取元數(shù)據(jù)的類型的編譯時信息。
// 指定兩個參數(shù),一個用來控制當(dāng)前類型找不到時是否拋出異常,另一個用來指示是否區(qū)分字符串大小寫 Type t = Type.GetType("Carlibrary.SportsCar", false, false) // 要得到外部程序集中類型的類型信息,字符串必須使用完全限定名,加上類型所在程序集的友好名字(逗號分隔) Type t = Type.GetType("Carlibrary.SportsCar, Carlibrary") // 要得到當(dāng)前程序集中嵌套枚舉的類型信息,使用+來標(biāo)記 Type t = Type.GetType("Carlibrary.JamesBondCar+SpyOption") // 獲取嵌套在JamesBondCar類中的枚舉類型(SpyOption)2.構(gòu)建自定義的元數(shù)據(jù)查看器
需要為反射導(dǎo)入命名空間:
using System.Reflection;2.1 反射方法
Type.GetMethods()返回一個System.Reflection.MethodInfo類型的數(shù)組,并使用foreach循環(huán)枚舉:
using System; using System.Reflection; public interface IA { void M1(); }; public interface IB { void M2(); }; class SportsCar : IA { public int a = 0; public double b = 3.14; public string S { get; set; } public void A(int a) { } public void B() { } public void C() { } public void M1() { } } class Program { static void ListMethods(Type t) { Console.WriteLine("*** Methods ***"); MethodInfo[] mi = t.GetMethods(); foreach (MethodInfo m in mi) Console.WriteLine($"->{m.Name}"); } static void Main() { SportsCar sc = new SportsCar(); Type t = sc.GetType(); ListMethods(t); Console.ReadKey(); } }output
*** Methods *** ->get_S ->set_S ->A ->B ->C ->ToString ->Equals ->GetHashCode ->GetType2.2 反射字段和屬性
static void ListFields(Type t) // 字段 { Console.WriteLine("*** Fields ***"); var fieldNames = from f in t.GetFields() select f.Name; foreach (var name in fieldNames) Console.WriteLine($"->{name}"); } static void ListProps(Type t) // 屬性 { Console.WriteLine("*** Methods ***"); var propsNames = from p in t.GetProperties() select p.Name; foreach (var name in propsNames) Console.WriteLine($"->{name}"); } static void Main() { SportsCar sc = new SportsCar(); Type t = sc.GetType(); ListFields(t); ListProps(t) Console.ReadKey(); }output
*** Fields *** ->a ->b *** Propertys *** ->S2.3 反射實現(xiàn)的接口
// 顯示當(dāng)前類實現(xiàn)的接口 static void ListInterfaces(Type t) { Console.WriteLine("*** Interfaces ***"); var ifacesNames = from i in t.GetInterfaces() select i.Name; foreach (var name in ifacesNames) Console.WriteLine($"->{name}"); } static void Main() { SportsCar sc = new SportsCar(); Type t = sc.GetType();; ListInterfaces(t); Console.ReadKey(); }output
*** Interfaces *** ->IA2.4 顯示其他信息
顯示關(guān)于傳入類型的各種統(tǒng)計信息(表示該類型是否是泛型,基類是什么,類型是否密封等等)
static void ListVariousStats(Type t) {Console.WriteLine("*** Various Statistics ***");Console.WriteLine($"Base class is: {t.BaseType}"); Console.WriteLine($"abstract? {t.IsAbstract}"); Console.WriteLine($"sealed? {t.IsSealed}"); Console.WriteLine($"generic? {t.IsGenericTypeDefinition}"); Console.WriteLine($"class? {t.IsClass}"); } static void Main() { SportsCar sc = new SportsCar(); Type t = sc.GetType(); ListVariousStats(t); Console.ReadKey(); }output
*** Various Statistics *** Base class is: System.Object abstract? False sealed? False generic? False class? True2.5 反射泛型信息
如果調(diào)用Type.GetType()來獲取泛型類型的元數(shù)據(jù)描述,就必須使用包含反引號(`)加上數(shù)值的語法來表示類型支持的類型參數(shù)個數(shù)
using System.Collection.Generic.List`1 // 反射List<T>元數(shù)據(jù)描述 using System.Collection.Generic.Dictionary`2 // 反射Dictionary<TKey, TValue>元數(shù)據(jù)描述2.6 反射方法參數(shù)和返回值
使用MethodInfo類型提供的ReturnType屬性和GetParameters()方法,列出方法的返回類型和輸入?yún)?shù)類型。
static void ListMethods(Type t) { Console.WriteLine("*** Methods ***"); MethodInfo[] mi = t.GetMethods(); foreach (MethodInfo m in mi) { string retVal = m.ReflectedType.FullName; string paramInfo = "( "; foreach (ParameterInfo pi in m.GetParameters()) { paramInfo += string.Format($"{pi.ParameterType} {pi.Name}"); } paramInfo += " )"; Console.WriteLine($"->{retVal} {m.Name} {paramInfo}"); } }output
*** Methods *** ->SportsCar get_S ( ) ->SportsCar set_S ( System.String value ) ->SportsCar A ( System.Int32 a ) ->SportsCar B ( ) ->SportsCar C ( ) ->SportsCar M1 ( ) ->SportsCar ToString ( ) ->SportsCar Equals ( System.Object obj ) ->SportsCar GetHashCode ( ) ->SportsCar GetType ( )或者使用以下語法(每一個XXXInfo類型都重寫了ToString()方法)
static void ListMethods(Type t) { Console.WriteLine("*** Methods ***"); var fieldNames = from f in t.GetMethods() select f; foreach (var name in fieldNames) Console.WriteLine($"->{name}"); }output
*** Methods *** ->System.String get_S() ->Void set_S(System.String) ->Void A(Int32) ->Void B() ->Void C() ->Void M1() ->System.String ToString() ->Boolean Equals(System.Object) ->Int32 GetHashCode() ->System.Type GetType()3.動態(tài)加載程序集
使用System.Reflection下的Assembly類,動態(tài)加載程序集,并找出關(guān)于程序集自身的屬性。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.IO; // For FileNotFoundException definition. namespace ExternalAssemblyReflector { class Program {加載程序集方法Load()與LoadFrom()的區(qū)別:
| 使用程序集友好名稱(其dll文件應(yīng)置于\bin\Debug目錄下) | 使用絕對路徑 |
| Assembly.Load("CarLibrary") | Assembly.LoadFrom("C:\MyApp\MyAsm.dll") |
4.反射共享程序集
Assembly.Load()的一種重載允許指定一個區(qū)域設(shè)置(為本地化程序集)、一個版本號和公鑰標(biāo)記值(為共享程序集)。整體來說,識別一個程序集的一組術(shù)語稱為顯示名稱(display name),顯示名稱以程序集友好名稱開頭。
Name(,Version = major.minor.build.revision)(,culture = culture token)(,PublicKeyToken = public key token)如:
Assembly a = Assembly.Load(@"CarLibrary, Version = 1.0.0.0, PublicKeyToken = null, Culture = """);可以使用System.Reflection命名空間提供的AssemblyName類型創(chuàng)建顯示名稱。
AssemblyName asmName; asmName = new AssemblyName(); asmName.Name = "CarLibrary"; Version v = new Version("1.0.0.0"); asmName.Version = v; Assembly a = Assembly.Load(asmName); namespace SharedAsmReflector {public class SharedAsmReflector {5.晚期綁定
晚期綁定(late binding)是一種創(chuàng)建一個給定類型的實例并在運行時調(diào)用其成員,而不需要在編譯時知道它存在的一種技術(shù)。即通過編程的方式在內(nèi)存中加載.NET程序集。
使用Activator.CreateInstance()方法建立一個晚期綁定類型的實例。
6.使用早期綁定反射特性
using AttributedCarLibrary; //引入AttributedCarLibrary namespace VehicleDescriptionAttributeReader { class Program { static void Main(string[] args) { Console.WriteLine("***** Value of VehicleDescriptionAttribute *****\n"); ReflectOnAttributesWithEarlyBinding(); Console.ReadLine(); } private static void ReflectOnAttributesWithEarlyBinding() { // 獲取Winnebago元數(shù)據(jù)描述 Type t = typeof(Winnebago); // 使用GetCustomAttributes(bool)獲取所有特性,返回一個對象數(shù)組,其中bool值控制是否擴展到繼承鏈 object[] customAtts = t.GetCustomAttributes(false); // 輸出描述 foreach (VehicleDescriptionAttribute v in customAtts) Console.WriteLine("-> {0}\n", v.Description); } } }7.使用晚期綁定反射特性
using System.Reflection; namespace VehicleDescriptionAttributeReaderLateBinding {class Program { static void Main(string[] args) { Console.WriteLine("***** Value of VehicleDescriptionAttribute *****\n"); ReflectAttributesUsingLateBinding(); Console.ReadLine(); }8.反射、晚期綁定使用背景
- 首先,可擴展的應(yīng)用程序必須提供一些輸入手段,允許用戶指定被插入的模塊,這需要動態(tài)加載
- 其次,可擴展的應(yīng)用程序必須要確定模塊是否支持正確的功能,這需要反射
- 最后,可擴展的應(yīng)用程序必須獲取一個需要的基礎(chǔ)架構(gòu)的引用并調(diào)用成員觸發(fā)底層功能,這需要晚期綁定?
9.反射的性能
反射時相當(dāng)強大的機制,允許在運行時發(fā)現(xiàn)并使用編譯時還不了解的類型及其成員,但是,反射也存在以下兩個缺點:
- 反射造成編譯時無法保證類型安全性。 由于反射嚴重依賴于字符串,所以會喪失編譯時的類型安全性。例如,執(zhí)行type.GetType("int");要求通過反射在程序集中查找名為“int”的類型,代碼會通過編譯,但運行時會返回null,因為CLR只知“System.Int32”,不知“int”。
- 反射速度慢。
- 使用反射時,類型及其成員的名稱在編譯時是未知的;要用字符串名稱標(biāo)識每個類及其成員,然后在運行時發(fā)現(xiàn)它們,即使用System.Reflection命名空間中的類型掃描程序集的元數(shù)據(jù)時,反射機制會不停的執(zhí)行字符串搜索,且通常執(zhí)行的是不區(qū)分大小的比較,這會進一步影響速度。
- 使用反射調(diào)用一個成員時。比如調(diào)用方法,首先必須將實參打包(pack)成一個數(shù)組;在內(nèi)部,反射必須將這些實參解包(unpack)到線程棧上。此外,在調(diào)用方法前,CLR必須檢查實參具有正確的數(shù)據(jù)類型。最后,CLR必須確保調(diào)用者有正確的安全權(quán)限來訪問被調(diào)用的成員。
改善方法:
- 讓類型從編譯時已知的基類型派生。運行時構(gòu)造派生類型的實例,將對它的引用放到基類型的變量中(利用轉(zhuǎn)型),再調(diào)用基類型定義的虛方法。
- 讓類型實現(xiàn)編譯時已知的接口。在運行時構(gòu)造類型的實例,將對它的引用放到接口類型的變量中(利用轉(zhuǎn)型),再調(diào)用接口定義的方法。
轉(zhuǎn)載于:https://www.cnblogs.com/jizhiqiliao/p/10649119.html
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Reflection的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java填坑系列之SparseArray
- 下一篇: Zabbix 4.2 发布:支持Prom