目錄
C#學習
.NET的體系結構
二次編譯
反射
什么是反射?
什么是Type?
什么是程序集?
反射API:
一,程序集
1, Load
2,LoadFrom
3,LoadFile
二,類型實例
1,無參構造
2,有參構造
3,私有構造
4,泛型類
三,方法調用
1,普通方法
2,靜態方法
3,私有方法
4,泛型方法
5,應用
四,屬性與字段
1,屬性讀寫
2,字段讀寫
3,應用
反射優缺點
優點
缺點
反射的價值是什么
C#學習
定義:面向對象,面向組件,類型安全,可以使用C#生成在.NET上運行的應用程序。
C#提供了語言構造來支持以上的定義,所以可以用來創建和使用軟件組件。
.NET的體系結構
C#在.NET上運行,.NET叫做,公共語言進行時(CLR)的虛擬執行系統和一組類庫.CLR 是 Microsoft 對公共語言基礎結構 (CLI) 國際標準的實現。 CLI 是創建執行和開發環境的基礎,語言和庫可以在其中無縫地協同工作。
用 C# 編寫的源代碼被編譯成符合 CLI 規范的中間語言 (IL)。 IL 代碼和資源(如位圖和字符串)存儲在擴展名通常為 .dll 的程序集中。 程序集包含一個介紹程序集的類型、版本和區域性的清單(metadata)。
執行 C# 程序時,程序集將加載到 CLR。 CLR 會直接執行實時編譯,將 IL 代碼轉換成本機指令。 CLR 可提供其他與自動垃圾回收、異常處理和資源管理相關的服務。
二次編譯
目的:一次編譯,多平臺使用,加一層中間層,更加靈活
由于系統很多,Mac,Win,Linux,其中Win還有32和64位的,為了做到一次編譯,多平臺運行,就需要二次編譯,C#源代碼被編譯成中間語言,存儲在擴展名通常為 .dll 的程序集中, 程序集包含一個介紹程序集的類型、版本和區域性的清單(metadata)。
這樣在不同的平臺,同一份編譯的中間語言,通過 CLR 轉換為不同平臺可執行機器指令,在不同平臺執行。
反射
什么是反射?
程序是用來處理數據的,但是程序本身也是由數據組成的,有關程序及其類型的數據,元數據(Metadata),保存在程序集之中,程序在運行中,可以查看其他的程序集和自身的Metadata,一個運行的程序查看本身的元數據或其他程序集的元數據的行為稱為反射。
什么是Type?
對于程序的類型,CLR都會創建一個包含這個類型的Type對象,程序中每遇到一個類型都會關聯到獨立的Type的對象,不管創建的類有多少實例,只有一個Type類的對象會關聯到這些所有的實例。
什么是程序集?
程序集是一個可以寄宿于 CLR 中的、擁有版本號的、自解釋、可配置的二進制文件,程序集的擴展名為 exe 或 dll。程序集是存放類型的集合,通過程序集可以獲取程序集內所有的類型信息
反射API:
一,程序集
如果沒有加載依賴,調用到使用依賴的對象時就會報錯。
1, Load
XXX:dll 名稱無后綴 從當前目錄加載 dll。開發環境 Bin ,發布程序是入口程序集文件當前目錄。
Assembly assembly = Assembly.Load("XXXX");
2,LoadFrom
XXX:已知程序集的文件名和路徑,會自動加載程序集的依賴程序集。
Assembly assembly = Assembly.LoadFrom("XXXX");
3,LoadFile
XXX:完整的dll路徑加載不會出錯,不會加載目標程序集所引用和依賴的其他程序集,需要自己控制并顯示加載所有依賴的程序集,如果沒有依賴項,使用的時候會錯。
Assembly assembly = Assembly.LoadFile("XXXX");
二,類型實例
1,無參構造
在獲取Assembly后獲取Type,根據Type創建實例,是Object類型的,為了編譯器可以通過需要進行類型轉換。
Type type = assembly.GetType("XXX.XXX");
object obj = (XXX)Activator.CreateInstance(type);
2,有參構造
在創建實例的時候,需要通過new object[]參數進行重載,此方法會根據 new object[] 里面的類型自動進行匹配構造函數
object obj = (XXX)Activator.CreateInstance(type, new object[] { "124", 123 });
3,私有構造
單例模式,在代碼里面為了避免外面進行實例化,在實現時都是以 private 修飾符對構造函數進行修飾,使其無法在外部進行實例化進行調用,但反射可以破壞這個規則。重點在 CreateInstance 方法,第二個參數 true
Assembly assembly = Assembly.LoadFrom("XXXX");
Type type = assembly.GetType("XXX.XXX");
object obj = Activator.CreateInstance(type,true);
4,泛型類
~n個占位符就代表幾個泛型,MakeGenericType 定義泛型類型,傳入 Type 數組即可
Assembly assembly = Assembly.LoadFrom("XXXX");
Type type = assembly.GetType("XXX~n");
Type typeNew = type.MakeGenericType(new Type[] { typeof(int), typeof(XXX),typeof(string) });
object obj = Activator.CreateInstance(typeNew, new object[] { "124", 123 });
三,方法調用
1,普通方法
Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX");// 類型名稱
object instance = Activator.CreateInstance(type); // 根據 type 實例對象
MethodInfo method = type.GetMethod("XXX");// 方法名稱
method.Invoke(instance,new object[] { "方法參數1", "方法參數2" });
2,靜態方法
靜態成員,Invoke 無需傳入實例對象,因為靜態成員在類里面,只有一份,確定了 Type 后就已經有了其靜態成員。
Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX");// 類型名稱
MethodInfo method = type.GetMethod("XXX");// 方法名稱
method.Invoke(null,new object[] { "方法參數1", "方法參數2" });
3,私有方法
私有方法在面向對象編程語言是不可以被外部調用的,但反射可以破壞這個規則調用私有方法,重點在 GetMethod 方法第二個參數為 BindingFlags.Instance|BindingFlags.NonPublic。
Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX");// 類型名稱
object instance = Activator.CreateInstance(type); // 根據 type 實例對象
MethodInfo method = type.GetMethod("XXX",BindingFlags.Instance|BindingFlags.NonPublic);// 方法名稱
method.Invoke(instance, new object[] { "方法參數1", "方法參數2" });
4,泛型方法
無論是泛型類還是泛型方法,都需要通過 MakeGenericXXXX 方法指定泛型的類型,需要注意的是泛型類在加載類型時需要占位符,而泛型方法不需要。
Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX~2");// 類型名稱,~2 占位符,代表幾個泛型
Type typeNew = type.MakeGenericType(new Type[] { typeof(string), typeof(int) });
object instance = Activator.CreateInstance(typeNew); // 根據 type 實例對象
MethodInfo method = type.GetMethod("XXX");// 方法名稱
MethodInfo methodNew = method.MakeGenericMethod(new Type[] { typeof(int) });
methodNew.Invoke(instance, new object[] { "方法參數1", "方法參數2" });
5,應用
MVC就是使用的反射機制,在程序啟動時,會掃描 controller 類型的類,會將其 Type 緩存起來,當有請求過來時,就會到緩存中找到對于的 Type 反射進行實例化并調用其方法(也就是 action)。說到這 mvc 的 filter 也就是在 調用方法前后加點料(反射 invoke 方法前后)。
四,屬性與字段
1,屬性讀寫
Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX.XXX");// 類型名稱
object instance = Activator.CreateInstance(type); //實例對象
foreach (var prop in type.GetProperties())
{if (prop.Name.Equals("Id")){prop.SetValue(instance, 1);Console.WriteLine(prop.GetValue(instance));}else if (prop.Name.Equals("Name")){prop.SetValue(instance, "張三");Console.WriteLine(prop.GetValue(instance));}
}
2,字段讀寫
Assembly assembly = Assembly.Load("XXX");// dll
Type type = assembly.GetType("XXX.XXX");// 類型名稱
object instance = Activator.CreateInstance(type); //實例對象
foreach (var field in type.GetFields())
{if (field.Name.Equals("id")){field.SetValue(instance, 1);Console.WriteLine(field.GetValue(instance));}else if (field.Name.Equals("name")){field.SetValue(instance, "張三");Console.WriteLine(field.GetValue(instance));}
}
3,應用
有一個 entity 與 entityDto:
public class Product
{public int ID { get; set; }public String Name { get; set; }
}
public class ProductDto
{public int ID { get; set; }public String Name { get; set; }
}
然后對 entity 承載的數據進行了實例化,并且依次賦值個了 entityDto
Product product = new Product()
{ID = 1,Name = "張三"
};
?
ProductDto productDto = new ProductDto();
productDto.ID = product.ID;
productDto.Name = product.Name;
對于上面的情況,還算簡單,但是如果字段過多,手動賦值要花費的時間就大大增加,于是我們可以采用反射機制來提高效率,自動賦值,實際應用時,我們可以使用T來進行封裝:
Product product = new Product()
{ID = 1,Name = "張三"
};
Type productType = typeof(Product); // Product Type
Type productDtoType = typeof(ProductDto);// ProductDto Type
object productDto = Activator.CreateInstance(productDtoType); // ProductDto Instance
foreach (var prop in productDtoType.GetProperties())
{// 依次拿取 dto 屬性名稱,在 Product Type 查找,并且從 Product Instance 獲取值object val = productType.GetProperty(prop.Name).GetValue(product);// ProductDto Instance Set Propertie Valprop.SetValue(productDto,val);
}
反射優缺點
優點
動態:反射就兩個字動態,就像 MVC 就是將方法的調用動態化了。數據庫或者封裝的處理業務邏輯的算法等,可以使用配置文件進行動態切換使用。
缺點
-
coding 復雜:面向對象靜態編碼,一兩行的代碼反射得寫個四五行。
-
避開編譯器檢查:平時寫代碼,我們寫錯了,編譯的時候會 error 提示我們,如果沒有編譯器寫得代碼不知道錯多少。但在反射里面,編譯器對類的操作不會進行檢查,這也是沒有辦法檢查,因為反射是動態的運行時的,不像普通編碼是靜態的。
反射的價值是什么
反射最直觀的區別是,由已有的固定類型,轉化為了字符串操作,且不需要在項目中進行引用(反射是動態的,依賴的是字符串)。因為依賴的是字符串,我們的程序才可配置化、才可易擴展,包括平時的框架開發都在大量使用反射(MVC、IOC、ORM等)。
學習參考自:菜鳥厚非