一、爲什麼要使用反射
程序集包含模塊,而模塊包含類型,類型又包含成員。反射則提供了封裝程序集、模塊和類型的對象。您可以使用反射動態地創建類型的實例,將類型綁定到現有對象,或從現有對象中獲取類型。然後,可以調用類型的方法或訪問其字段和屬性。
二、反射機制的原理
審查元數據並收集關於它的類型信息的能力。
元數據是一種二進制信息,用以對存儲在公共語言運行庫可移植可執行文件 (PE) 文件或存儲在內存中的程序進行描述。將您的代碼編譯爲 PE 文件時,便會將元數據插入到該文件的一部分中。
元數據以非特定語言的方式描述在代碼中定義的每一類型和成員。元數據存儲以下信息:
三、反射的作用
反射通常具有以下用途:
-
使用 Assembly 定義和加載程序集,加載在程序集清單中列出的模塊,以及從此程序集中查找類型並創建該類型的實例。
-
使用 Module 瞭解如下的類似信息:包含模塊的程序集以及模塊中的類等。您還可以獲取在模塊上定義的所有全局方法或其他特定的非全局方法。
-
使用 ConstructorInfo 瞭解以下信息:構造函數的名稱、參數、訪問修飾符(如public 或private)和實現詳細信息(如 abstract 或virtual)等。使用 Type 的 GetConstructors 或 GetConstructor 方法來調用特定的構造函數。
-
使用 MethodInfo 瞭解以下信息:方法的名稱、返回類型、參數、訪問修飾符(如 public 或 private)和實現詳細信息(如 abstract 或 virtual)等。使用 Type 的 GetMethods 或 GetMethod 方法來調用特定的方法。
-
使用 FieldInfo 瞭解以下信息:字段的名稱、訪問修飾符(如 public 或private)和實現詳細信息(如static)等;並獲取或設置字段值。
-
使用 EventInfo 來了解如下的類似信息:事件的名稱、事件處理程序數據類型、自定義屬性、聲明類型和反射類型等;並添加或移除事件處理程序。
-
使用 PropertyInfo 來了解如下的類似信息:屬性的名稱、數據類型、聲明類型、反射類型和只讀或可寫狀態等;並獲取或設置屬性值。
-
使用 ParameterInfo 來了解如下的類似信息:參數的名稱、數據類型、參數是輸入參數還是輸出參數,以及參數在方法簽名中的位置等。
-
當您在一個應用程序域的僅反射上下文中工作時,請使用 CustomAttributeData 來了解有關自定義屬性的信息。使用 CustomAttributeData,您不必創建屬性的實例就可以檢查它們。
-
System.Reflection.Emit 命名空間的類提供了一種特殊形式的反射,使您能夠在運行時構造類型。
-
反射也可用於創建稱作類型瀏覽器的應用程序,它使用戶能夠選擇類型,然後查看有關選定類型的信息。
-
反射還有其他一些用途。JScript 等語言編譯器使用反射來構造符號表。System.Runtime.Serialization 命名空間中的類使用反射來訪問數據並確定要持久保存的字段。System.Runtime.Remoting 命名空間中的類通過序列化來間接地使用反射。
以上摘自http://msdn.microsoft.com/zh-cn/library/f7ykdhsy%28v=vs.80%29
四、一些小知識
1、類型(Type) 對象是什麼
比如 object x; x是對象,object就是它的類型,在程序中如何描述類型這個概念呢?就是Type(System.Type)。要獲取某個類的類型可以用typeof()操作符
object a;object b;
DataTable t;
Type aType = typeof(object);Type bType = typeof(object);tType = typeof(DataTable);
aType==bType!=tType;
2、程序集(Assembly)
就是你IDE生成的.exe或.dll文件的運行時就叫程序集。所有的代碼都在程序集中。你可以通過Assembly.Load()系列函數動態加載程序集(這一步是動態+載代碼的前提,因爲所有的代碼都在程序集中)。
3、動態加載
我們普通調用代碼是: 對象名.方法名(參數列表);
class a{
void func(int x){}
public static void Main(string[] args)
{
//創建對象
a a1 = new a();
//調用函數
a1.func(1);
}
}
用反射動態調用代碼是
//加載程序集
System.Reflection.Assembly asm = Assembly.LoadFile(assemblyPath);
//獲取類型
Type aType = asm.GetType( "名字空間.類名 ");
//獲取沒有參數的構造函數
System.Reflection.ConstructorInfo conn = t.GetConstructor(new Type[0]);
//調用沒有參數的構造函數,Invoke返回object它其是a類
object a1 = conn.Invoke(new object[0]);
//獲取參數類型爲int,函數名爲func的方法
MethodInfo method = t.GetMethod( "func ",new Type[]{typeof(int)});
//在a1上調用func方法,參數爲1
method.Invoke(a1,new object[]{1});
動態調用(後一種方法)比靜態調用更復雜,而且效率大概低20倍(網上有個哥們好像測試過)。只有在特殊的時候才調用動態加載動態調用---比如,你的主程序啓動的時候子模塊還沒有,要根據登陸信息下載子模塊代碼並調用子模塊代碼,就只能用動態+載來實現了.
4、元數據
.net生成的IL代碼中標明瞭在這個(exe,dll)文件中所有的class(類) method(方法)Attribute(屬性) Property(類屬性)的簽名和調用方法信息,這些信息就叫做元數據。所謂的Reflection反射,就是利用元數據,可以瞭解到某個assembly(基本等同文件)中的class。。。。。 (就是上面那一串咚咚)信息和調用方法。
5、.net framework
在.net framework中反射類基本都在System.Reflection中。System.Type是反射的核心類.
與它相關的還有System.Attribute命名空間。
五、實例
1、Assembly的使用
Assembly是一個包含來程序的名稱,版本號,自我描述,文件關聯關係和文件位置等信息的一個集合。在.net框架中通過Assembly類來支持,該類位於System.Reflection下,物理位置位於:mscorlib.dll。我們可以通過Assembly的信息來獲取程序的類,實例等編程需要用到的信息。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
People people;//這種使用方式可以獲取相應類中的變量、方法等元素
string assemblyName = @"ConsoleApplication5";
string strongClassName = @"ConsoleApplication5.Male";//可以把這裏的聲明看作是路徑:命名空間下的類名
people = (People)Assembly.Load(assemblyName).CreateInstance(strongClassName);//這裏也可以使用Male或者Female變量來獲取相應的屬性及方法
Console.WriteLine(people.name);
//Assembly ass = Assembly.Load(assemblyName);//這種使用方式可以獲取程序的名稱,版本號,自我描述,文件關聯關係和文件位置等信息的一個集合
//Assembly ass1 = Assembly.LoadFrom(@"D:\work\myTest\myTest\bin\myTest.dll");
////獲取程序集顯示名稱
//Console.WriteLine(ass1.FullName);
////獲取程序集中定義的類型
//Type[] types = ass.GetTypes();
//foreach (Type t in types)
//{
// Console.WriteLine(t.FullName);
//}
Console.ReadKey();
}
}
class People
{
public string name;
}
class Male : People
{
public Male()
{
name = "你好";
}
public string sex = "男";
public string hello()
{
return "我是"+sex+"生";
}
}
class Female : People
{
public Female()
{
name = "Hello";
}
public string sex = "女";
public string hello()
{
return "我是" + sex + "生";
}
}
}
2、Module的使用
3、ConstructorInfo 的使用
繼續試用上面的例子
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
Assembly ass = Assembly.Load("ConsoleApplication5");
Assembly ass1 = Assembly.LoadFrom(@"D:\work\myTest\myTest\bin\myTest.dll");
Type type = ass.GetType("ConsoleApplication5.People");
//將得到的類型傳給一個新建的構造器類型變量
ConstructorInfo constructor = type.GetConstructor(new Type[0]);
//使用構造器對象來創建對象
object obj = constructor.Invoke(new Object[0]);
//輸出對象類型
Console.WriteLine(obj);
}
}
}
4、MethodInfo 的使用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace MethodInfoInvokeDemo
{
public class ReflectTest
{
public void MethodWithNoParaNoReturn()
{
Console.WriteLine("不帶參數且不返回值的方法");
}
public string MethodWithNoPara()
{
Console.WriteLine("不帶參數且有返回值的方法");
return "MethodWithNoPara";
}
public string Method1(string str)
{
Console.WriteLine("帶參數且有返回值的方法");
return str;
}
public string Method2(string str, int index)
{
Console.WriteLine("帶參數且有返回值的方法");
return str + index.ToString();
}
public string Method3(string str, out string outStr)
{
outStr = "bbbb";
Console.WriteLine("帶參數且有返回值的方法");
return str;
}
public static string StaticMethod()
{
Console.WriteLine("靜態方法");
return "cccc";
}
}
class Program
{
static void Main(string[] args)
{
Type type = typeof(ReflectTest);
object reflectTest = Activator.CreateInstance(type);
//不帶參數且不返回值的方法的調用
MethodInfo methodInfo = type.GetMethod("MethodWithNoParaNoReturn");
methodInfo.Invoke(reflectTest, null);
Console.WriteLine();
//不帶參數且有返回值的方法的調用
methodInfo = type.GetMethod("MethodWithNoPara");
Console.WriteLine(methodInfo.Invoke(reflectTest, null).ToString());
Console.WriteLine();
//帶參數且有返回值的方法的調用
methodInfo = type.GetMethod("Method1", new Type[] { typeof(string) });
Console.WriteLine(methodInfo.Invoke(reflectTest, new object[] { "測試" }).ToString());
Console.WriteLine();
//帶多個參數且有返回值的方法的調用
methodInfo = type.GetMethod("Method2", new Type[] { typeof(string), typeof(int) });
Console.WriteLine(methodInfo.Invoke(reflectTest, new object[] { "測試", 100 }).ToString());
//Console.WriteLine();
//methodInfo = type.GetMethod("Method3", new Type[] { typeof(string), typeof(string) });
//string outStr = "";
//Console.WriteLine(methodInfo.Invoke(reflectTest, new object[] { "測試", outStr }).ToString());
Console.WriteLine();
//靜態方法的調用
methodInfo = type.GetMethod("StaticMethod");
Console.WriteLine(methodInfo.Invoke(null, null).ToString());
Console.ReadKey();
}
}
}