c#基礎語言編程-程序集和反射

程序集

這裏寫圖片描述
什麼是程序集?

  • 1.程序集(assembly)是一個及一個以上託管模塊,以及一些資源文件的邏輯組合。
  • 2.程序集是組件複用,以及實施安全策略和版本策略的最小單位。
  • 3.程序集是包含一個或者多個類型定義文件和資源文件的集合。在程序集包含的所有文件中,有一個文件用於保存清單。(清單是元數據部分中一組數據表的集合,其中包含了程序集中一部分文件的名稱,描述了程序集的版本,語言文化,發佈者,共有導出類型,以及組成該程序集的所有文件)。
  • 4、在編譯應用程序中,所創建的CIL代碼存儲在一個程序集中,程序集包括可執行的應用程序文件(.exe擴展名文件)和其他應用程序使用的庫(.dll擴展名文件)。
  • 簡單的說在.NET生成的dll和exe都是程序集,但是c++生成的就不是了。程序集包含資源文件,類型元數據(描述在代碼中定義的每一類型和成員,二進制形式)、IL代碼(這些都被裝在exe或dll中),每個程序集都有自己的名稱、版本等信息。這些信息可以通過AssemblyInfo.cs文件來自己定義。
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// 有關程序集的常規信息通過以下
// 特性集控制。更改這些特性值可修改
// 與程序集關聯的信息。
[assembly: AssemblyTitle("AssemblyDemo")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AssemblyDemo")]
[assembly: AssemblyCopyright("Copyright ©  2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// 將 ComVisible 設置爲 false 使此程序集中的類型
// 對 COM 組件不可見。如果需要從 COM 訪問此程序集中的類型,
// 則將該類型上的 ComVisible 特性設置爲 true。
[assembly: ComVisible(false)]

// 如果此項目向 COM 公開,則下列 GUID 用於類型庫的 ID
[assembly: Guid("e7da9959-0c97-444c-aa40-6d9bbf728068")]

// 程序集的版本信息由下面四個值組成:
//
//      主版本
//      次版本 
//      內部版本號
//      修訂號
//
// 可以指定所有這些值,也可以使用“內部版本號”和“修訂號”的默認值,
// 方法是按如下所示使用“*”:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

AssemblyInfo.cs

這裏寫圖片描述
程序集的優點:

  • 解決版本控制問題,程序只需要應用必要的程序集,減少了編碼量(例如log4net.dll),程序的尺寸
  • 解決dll衝突(Windows歷史上著名的 dll地獄)
  • 以在程序集中封裝一些代碼,提供必要的接口,供引用該程序集的項目使用

程序集常用的方法
Assembly.Load()方法,Assembly.LoadFrom()方法,Assembly.LoadFile()方法的區別!
1,Assembly.Load()
這個方法通過程序集的長名稱(包括程序集名,版本信息,語言文化,公鑰標記)來加載程序集的,會加載此程序集引用的其他程序集,一般情況下都應該優先使用 這個方法,他的執行效率比LoadFrom要高很多,而且不會造成重複加載的問題(原因在第2點上說明)
使用這個方法的時候, CLR會應用一定的策略來查找程序集,實際上CLR按如下的順序來定位程序集:
⑴如果程序集有強名稱,在首先在全局程序集緩(GAC)中查找程序集。
⑵如果程序集的強名稱沒有正確指定或GAC中找不到,那麼通過配置文件中的元素指定的URL來查找
⑶如果沒有指定強名稱或是在GAC中找不到,CLR會探測特定的文件夾:
假設你的應用程序目錄是C:\AppDir,元素中的privatePath指定了一個路徑Path1,你要定位的程序集是AssemblyName.dll則CLR將按照如下順序定位程序集
C:\AppDir\AssemblyName.dll
C:\AppDir\AssemblyName\AssemblyName.dll
C:\AppDir\Path1\AssemblyName.dll
C:\AppDir\Path1\AssemblyName\AssemblyName.dll
如果以上方法不能找到程序集,會發生編譯錯誤,如果是動態加載程序集,會在運行時拋出異常!
2,Assembly.LoadFrom()
這個方法從指定的路徑來加載程序集,實際上這個方法被調用的時候,CLR會打開這個文件,獲取其中的程序集版本,語言文化,公鑰標記等信息,把他們傳遞給 Load方法,接着,Load方法採用上面的策略來查找程序集。如果找到了程序集,會和LoadFrom方法中指定的路徑做比較,如果路徑相同,該程序集 會被認爲是應用程序的一部分,如果路徑不同或Load方法沒有找到程序集,那該程序集只是被作爲一個“數據文件”來加載,不會被認爲是應用程序的一部分。 這就是在第1點中提到的Load方法比LoadFrom方法的執行效率高的原因。另外,由於可能把程序集作爲“數據文件”來加載,所以使用 LoadFrom從不同路徑加載相同程序集的時候會導致重複加載。當然這個方法會加載此程序集引用的其他程序集。
3,Assembly.LoadFile()
這個方法是從指定的文件來加載程序集,和上面方法的不同之處是這個方法不會加載此程序集引用的其他程序集!
結論:一般大家應該優先選擇Load方法來加載程序集,如果遇到需要使用LoadFrom方法的時候,最好改變設計而用Load方法來代替!
另:Assembly.LoadFile 與 Assembly.LoadFrom的區別
1、Assembly.LoadFile只載入相應的dll文件,比如Assembly.LoadFile(“abc.dll”),則載入abc.dll,假如abc.dll中引用了def.dll的話,def.dll並不會被載入。
Assembly.LoadFrom則不一樣,它會載入dll文件及其引用的其他dll,比如上面的例子,def.dll也會被載入。
2、用Assembly.LoadFrom載入一個Assembly時,會先檢查前面是否已經載入過相同名字的Assembly,比如abc.dll有兩個版本(版本1在目錄1下,版本2放在目錄2下),程序一開始時載入了版本1,當使用Assembly.LoadFrom(“2\abc.dll”)載入版本2時,不能載入,而是返回版本1。Assembly.LoadFile的話則不會做這樣的檢查,比如上面的例子換成Assembly.LoadFile的話,則能正確載入版本2。
LoadFile:加載指定路徑上的程序集文件的內容。LoadFrom: 根據程序集的文件名加載程序集文件的內容。
區別:
LoadFile 方法用來來加載和檢查具有相同標識但位於不同路徑中的程序集.但不會加載程序的依賴項。
LoadFrom 不能用於加載標識相同但路徑不同的程序集。
簡而言之:如果動態引用其他廠家的dll,可以用LoadFrom,因爲廠家的dll還以引用其他的dll,如果自己寫的小dll,沒有依賴項,則用LoadFile,但是這樣很少用。最新版的只支持LoadFrom了。

反射

反射就是動態獲取程序集中的元數據(提供程序集的類型信息)的功能。也就是動態獲取程序集中的元數據來操作類型的。比如咱們使用的vs中的智能提示,就是通過反射獲取類的方法、屬性的。程序集包含模塊,而模塊包含類型,類型又包含成員。 反射則提供了封裝程序集、模塊和類型的對象。 您可以使用反射動態地創建類型的實例,將類型綁定到現有對象,或從現有對象中獲取類型。 然後,可以調用類型的方法或訪問其字段和屬性。
表示類型聲明:類類型、接口類型、數組類型、值類型、枚舉類型、類型參數、泛型類型定義,以及開放或封閉構造的泛型類型。
這裏寫圖片描述
可以通過以下兩種方式獲得Type:
1.通過類獲得Type: Type t=typeof(Person);
Assembly中對type的類型的獲取
調用Assembly的GetExportedTypes方法可以得到Assembly中定義的所有的public類型。
調用Assembly的GetTypes()方法可以得到Assembly中定義的所有的類型。
調用Assembly的GetType(name)方法可以得到Assembly中定義的全名爲name的類型信息。如: Type type = assembly.GetType( ” MyAssembly.Person ” );
動態創建對像
Activator.CreateInstance(Type t)會動態調用類的無參構造函數創建一個對象,返回值就是創建的對象,如果類沒有無參構造函數就會報錯。
GetConstructor(參數列表);//這個是找到帶參數的構造函數。例如: object o = Activator.CreateInstance(type, ” wolf ” , 22 , ” 未知 ” ); // 實例化
Type函數介紹
屬性:
•type.Assembly:獲取type所在的程序集對象
•type.FullName:獲取type對象對應的類的全名稱
•type.Name: 獲取type對象對應類的 名稱
•type.IsArray: 判斷type是否爲一個數組類
•type.IsEnum: 判斷type是否爲一個枚舉類
方法:
•type.IsAssignableFrom(Type i):判斷type是否實現了接口i
•type.IsSubclassOf(Type father):判斷type是否繼承了father
•type.IsInstanceOfType(objecto):判斷o是否爲type類的實例
•type.GetFiled(“gender”):獲取type中名爲gender的字段對象
•type.GetMethod(“SayHi”):獲取type中名爲SayHi的方法對象
•type.GetProperty(“Age”):獲取type中名爲Age的屬性對象

//動態加載一個程序集
 Assembly assembly = Assembly.LoadFile(@"C:\02TestDll.dll");
//2.獲取剛剛加載的程序集中的所有的類型
//assembly.GetType()  等價於  typeof(Assembly),不能獲取某個程序集中國你的所有類型那個
//GetTypes()獲取了所有的類型
 Type[] types = assembly.GetTypes();
////只獲取那些public的類型
Type[] types = assembly.GetExportedTypes();
 //加入程序集中有多個類,只獲取Person類的Type
//GetType()方法有重載,選擇第二個重載,參數表示是要獲取的類型的“完全限定名稱”,即:命名空間.類名
//這裏拿到了Type,其實就等價於typeof(Person)或者是:p.GetType();
 Type personType = assembly.GetType("_02TestDll.Person");
//獲取所有的方法:personType.GetMethods();
////調用一個無參數,無返回值的方法
 MethodInfo method = personType.GetMethod("SayHi");            Console.WriteLine(method.Name);
//通過反射來創建一個Person類型的對象{其實就是通過Person的Type來創建一個Person對象}
object objPerson = Activator.CreateInstance(personType);
//調用這個方法
method.Invoke(objPerson, null);
//調用帶參數,帶返回值的方法
//1>找到對應的方法
 MethodInfo method = personType.GetMethod("Add");
object obj = Activator.CreateInstance(personType);
 //2>調用
 object result = method.Invoke(obj, new object[] { 102, 203 });
//調用重載的方法
 //1>找到對應的方法
MethodInfo method = personType.GetMethod("Add", new Type[] { typeof(int), typeof(int), typeof(int) });
 object obj = Activator.CreateInstance(personType);
//2>調用
int r = (int)method.Invoke(obj, new object[] { 1, 2, 3 });
 #region  通過反射獲取類的屬性,並賦值
//1.獲取Name屬性
PropertyInfo property = personType.GetProperty("Name");
object obj = Activator.CreateInstance(personType);
//2.爲屬性賦值
            property.SetValue(obj, "閆劉盤", null);
 //3.獲取屬性
 string name = property.GetValue(obj, null).ToString();
 #endregion

//#region 手動查找類型的構造函數,並且調用該構造函數來創建類型的對象
//查找到了對應的構造函數,但是還沒有調用
ConstructorInfo ctor = personType.GetConstructor(new Type[] { typeof(string), typeof(int), typeof(string) });
//開始調用構造函數
object obj = ctor.Invoke(new object[] { "hpp", 16, "[email protected]" });
bool IsAssignableFrom(Type c):(直譯:是否可以從c賦值)判斷當前的類型的變量是不是可以接受c類型變量的賦值。表示可以將Student類型賦值給Person類型,因爲Student類型繼承自Person類
bool IsInstanceOfType(object o):判斷對象o是否是當前類的實例(當前類可以是o的類、父類、接口)
bool IsSubclassOf(Type c):判斷當前類是否是類c的子類。只驗證類與類之間的父子類關係,接口不包含。
IsAbstract   判斷是否爲抽象的,含接口

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章