解析
反射技術(shù)在運行時可以獲取程序集中每個類型的成員,包括字段、方法、屬性、事件等,并進一步獲取這些成員的詳細信息。反射技術(shù)還可以動態(tài)載入外部程序集(私有程序集或共享程序集),獲取程序集中類型的相關(guān)數(shù)據(jù)。有意思的是從外部動態(tài)載入的程序集還可通過晚期綁定,創(chuàng)建外部程序集中某類型的實例(對象),并且可以進一步調(diào)用其成員(如方法和屬性)。在這個過程中,并不知道外部程序集的任何信息(甚至不知道該程序集是否存在)。
簡而言之,反射技術(shù)以編程的方式獲取程序集的元數(shù)據(jù)信息,通常情況下,只能通過ildasm.exe程序載入程序集或模塊,才能讀取其元數(shù)據(jù)。 NET的反射技術(shù)從程序集中獲取各種細節(jié)類型元數(shù)據(jù)(如FieldInfo類描述了字段的細節(jié))實例,并做進一步的操作,而這些細節(jié)類型大多數(shù)屬于 System.Reflection命名空間。
說明:ildasm.exe是.Net Framework自帶的程序,用于查看程序集的IL代碼、元數(shù)據(jù)等信息。假設(shè)C盤是系統(tǒng)分區(qū),則ildasm.exe程序通常位于C:\WINDOWS \Microsoft.NET\ Framework\v2.0.50727目錄下。
System.Reflection命名空間包含了很多與反射有關(guān)的類型,通過這些類型可以獲取指定類型的所有細節(jié)。本題要求獲取指定類型中方法的完整信息,而System.Reflection命名空間下關(guān)于方法的類型為MethodInfo類型和ParameterInfo類型,所以只需要獲取指定類型中方法的這2個類相關(guān)的對象即可。要達到這個目的,需要獲取表示指定類型元數(shù)據(jù)的Type對象,Type對象即System.Type類的實例,該對象的成員可以返回System.Reflection命名空間下的類型,包括MethodInfo類型和ParameterInfo類型。在前面的示例中使用了對象的GetType方法獲取類型名稱,除此之外,還有多種獲取Type對象的方法,如以下代碼所示:
+展開-C#
//調(diào)用對象的GetType方法獲取Type對象的引用
Type 對象引用變量 = 對象.GetType();
//調(diào)用Type類GetType靜態(tài)方法的不同重載版本
Type 對象引用變量 = System.Type.GetType("類型的全飾名稱");
Type 對象引用變量 = System.Type.GetType("類型的全飾名稱",
是否拋出異常, 是否不區(qū)分大小寫);
Type 對象引用變量 = System.Type.GetType("類型的全飾名稱,
類型所屬程序集的友好名稱");
//使用C#的typeof運算符
Type 對象引用變量 = typeof(類型名稱);
以上代碼,調(diào)用Type類的GetType()靜態(tài)方法,其中第3個重載版本用于獲取外部私有程序集類型的Type對象。當獲取指定類型的Type對象后,就可以通過其成員返回MethodInfo類型和ParameterInfo類型,以達到獲取方法完整信息的目的,如以下代碼所示:
+展開-C#
using System;
using System.Reflection;
MethodInfo[] 方法數(shù)組 = Type.GetType("類型的全飾名稱", false, false);
使用foreach語句遍歷方法數(shù)組的每個子項,即可訪問類型中方法的所有信息;
//在foreach語句中,調(diào)用方法數(shù)組子項的GetParameters方法,可返回ParameterInfo類型的參數(shù)數(shù)組
ParameterInfo參數(shù)數(shù)組 = 方法數(shù)組子項.GetParameters();
使用foreach語句遍歷參數(shù)數(shù)組的每個子項,即可訪問方法中參數(shù)的所有信息;
以上代碼使用了foreach語句,可訪問方法中參數(shù)的所有信息。
注意:默認情況下,反射只能獲取公共成員的信息,而訪問非公共成員可能會帶來安全危險。因此,訪問非公共成員的代碼需要帶有適當標志的 ReflectionPermission。此外,某些任務(wù)(例如執(zhí)行非托管代碼和序列化對象)還需要SecurityPermission。
面試例題7:如何利用反射獲取當前程序集指定類型的信息?
考點:反射技術(shù)獲取類型信息的方法以及獲取類型成員集合的方法。
出現(xiàn)頻率:★★
解答
獲取指定類型的信息只需要獲取該類型的Type對象,然后調(diào)用其成員即可。獲取執(zhí)行代碼封裝于ClassB類的靜態(tài)方法Ref()中,用戶輸入不同的值,反射不同類型的詳細信息。在目錄下新建一個程序文件,并命名為ClassRef.cs,編寫代碼如代碼7.7所示。
代碼7.7 反射指定類型的信息:ClassRef.cs
+展開-C#
using System;
//導(dǎo)入相應(yīng)的命名空間
using System.Reflection;
class ClassRef
{
static void Main(string[] args)
{
while (true)
{
Console.Write("請輸入所檢測的類型名稱:");
//接收用戶輸入值并賦值給input變量
string input = Console.ReadLine();
//如果用戶輸入"quit",則跳出循環(huán)
if (input == "quit")
{
break;
}
try
{
//調(diào)用Type類的靜態(tài)方法GetType,并將Type對象引用返回給tp變量
Type tp = Type.GetType(input, false, false);
//調(diào)用ClassB的Ref靜態(tài)方法,并傳遞tp對象
ClassB.Ref(tp);
}
//捕獲空對象引用異常
catch (NullReferenceException e)
{
//輸出異常信息
Console.WriteLine("異常信息:{0}", e.Message);
}
//捕獲一般異常
catch (Exception e)
{
//輸出異常信息
Console.WriteLine("異常信息:{0}", e.Message);
}
}
}
}
//定義2個接口類型IClassA和IClassB
public interface IClassA
{
string MethodA(string s);
}
public interface IClassB
{
string Name
{
get;
}
}
//定義ClassA,該類繼承于接口類型IClassA和IClassB
class ClassA : IClassA,IClassB
{
public string _name;
public string Name
{
get
{
return _name;
}
}
//定義internal權(quán)限的MethodA方法
public string MethodA(string s)
{
_name = s;
return _name;
}
//定義public權(quán)限的MethodB方法
public ClassA(string s)
{
Console.WriteLine("所接收的參數(shù)是:{0}", s);
}
}
class ClassB
{
string _name;
//定義internal權(quán)限的MethodB方法
internal string MethodB(string s)
{
_name = s;
return _name;
}
//定義靜態(tài)方法Ref,接收1個Type類型的參數(shù)
public static void Ref(Type tp)
{
//輸出Type對象的基本屬性
string FullName = tp.FullName;
Console.WriteLine("\n\t============={0}類型的信息=============", FullName);
Console.WriteLine("{0}是泛型類型嗎?->{1}", FullName, tp.IsGenericType);
Console.WriteLine("{0}是接口類型嗎?->{1}", FullName, tp.IsInterface);
Console.WriteLine("{0}是類類型嗎?->{1}",FullName,tp.IsClass);
Console.WriteLine("{0}是COM對象嗎?->{1}", FullName, tp.IsCOMObject);
Console.WriteLine("{0}是public訪問類型嗎?->{1}", FullName, tp.IsPublic);
Console.WriteLine("{0}是密封類型嗎?->{1}", FullName, tp.IsSealed);
Console.WriteLine("{0}是值類型嗎?->{1}", FullName, tp.IsValueType);
//獲取Type對象的所有公共成員并保存到mi數(shù)組
MemberInfo[] mi = tp.GetMembers();
//遍歷并輸出mi數(shù)組所有的子項屬性
foreach (MemberInfo m in mi)
{
Console.WriteLine("\t成員類別->{0},名稱->{1}",m.MemberType, m.Name);
}
//獲取Type對象所支持的接口并保存到Itp數(shù)組
Type[] Itp = tp.GetInterfaces();
//判斷Itp數(shù)組是否有子項,如果有則輸出子項屬性
if (Itp.Length != 0)
{
foreach (Type t in Itp)
{
Console.WriteLine("{0}實現(xiàn)的接口類型->{1}", FullName, t.FullName);
}
}
else
{
Console.WriteLine("{0}不實現(xiàn)的任何接口類型", FullName);
}
}
}