反射(Reflection)是.NET中的重要機制,通過放射,可以在運行時獲得.Net中每一個類型(包括類、結構、委托、接口和枚舉等)的成員,包括方法、屬性、事件,以及構造函數等。還可以獲得每個成員的名稱、限定符和參數等。有了反射,即可對每一個類型了如指掌。如果獲得了構造函數的信息,即可直接創建對象,即使這個對象的類型在編譯時還不知道。
1、.Net可執行應用程序結構
程序代碼在編譯後生成可執行的應用,我們首先要了解這種可執行應用程序的結構。
應用程序結構分為應用程序域—程序集—模塊—類型—成員幾個層次,公共語言運行庫加載器管理應用程序域,這種管理包括將每個程序集加載到相應的應用程序域以及控制每個程序集中類型層次結構的內存布局。
程序集包含模塊,而模塊包含類型,類型又包含成員,反射則提供了封裝程序集、模塊和類型的對象。我們可以使用反射動態地創建類型的實例,將類型綁定到現有對象或從現有對象中獲取類型,然後調用類型的方法或訪問其字段和屬性。反射通常具有以下用途。
(1)使用Assembly定義和加載程序集,加載在程序集清單中列出模塊,以及從此程序集中查找類型並創建該類型的實例。
(2)使用Module了解包含模塊的程序集以及模塊中的類等,還可以獲取在模塊上定義的所有全局方法或其他特定的非全局方法。
(3)使用ConstructorInfo了解構造函數的名稱、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法來調用特定的構造函數。
(4)使用MethodInfo了解方法的名稱、返回類型、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法來調用特定的方法。
(5)使用FIEdInfo了解字段的名稱、訪問修飾符(如public或private)和實現詳細信息(如static)等,並獲取或設置字段值。
(6)使用EventInfo了解事件的名稱、事件處理程序數據類型、自定義屬性、聲明類型和反射類型等,添加或移除事件處理程序。
(7)使用PropertyInfo了解屬性的名稱、數據類型、聲明類型、反射類型和只讀或可寫狀態等,獲取或設置屬性值。
(8)使用ParameterInfo了解參數的名稱、數據類型、是輸入參數還是輸出參數,以及參數在方法簽名中的位置等。
System.Reflection.Emit命名空間的類提供了一種特殊形式的反射,可以在運行時構造類型。
反射也可用於創建稱為類型浏覽器的應用程序,使用戶能夠選擇類型,然後查看有關選定類型的信息。
此外,JScript等語言編譯器使用反射來構造符號表。System.Runtime.Serialization命名空間中的類使用反射來訪問數據並確定要永久保存的字段,System.Runtime.Remoting命名空間中的類通過序列化來間接地使用反射。
2、反射技術示例
下面是反射技術的示例,我們可以在程序去得時動態實例化對象,獲得對象的屬性,並調用對象的方法。
1Namespace ReflectionExample
2{
3 class Class1
4 {
5 [STAThread]
6 static void Main (string [ ] args)
7 {
8 System.Console.WriteLine(“列出程序集中的所有類型”);
9 Assembly a = Assembly.LoadFrom (“ReflectionExample.exe”);
10 Type[ ] mytypes = a.GetTypes( );
11
12 Foreach (Type t in mytypes)
13 {
14 System.Console.WriteLine ( t.Name );
15 }
16 System.Console.ReadLine ( );
17 System.Console.WriteLine (“列出HellWord中的所有方法” );
18 Type ht = typeof(HelloWorld);
19 MethodInfo[] mif = ht.GetMethods();
20 foreach(MethodInfo mf in mif)
21 {
22 System.Console.WriteLine(mf.Name);
23 }
24 System.Console.ReadLine();
25 System.Console.WriteLine("實例化HelloWorld,並調用SayHello方法");
26 Object obj = Activator.CreateInstance(ht);
27 string[] s = {"zhenlei"};
28 Object bojName = Activator.CreateInstance(ht,s);
29 BindingFlags flags = (BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance|BindingFlags.DeclaredOnly);
30 MethodInfo msayhello = ht.GetMethod("SayHello");
31 msayhello.Invoke(obj,null);
32 msayhello.Invoke(objName,null);
33 System.Console.ReadLine();
34 }
35 }
36}
1using System;
2namespace ReflectionExample
3{
4 public class HelloWorld
5 {
6 string myName = null;
7 public HelloWorld(string name)
8 {
9 myName = name;
10 }
11 public HelloWorld() : this(null)
12 {}
13 public string Name
14 {
15 get
16 {
17 return myName;
18 }
19 }
20 public void SayHello()
21 {
22 if(myName == null)
23 {
24 System.Console.WriteLine("Hello World");
25 }
26 else
27 {
28 System.Console.WriteLine("Hello," + myName);
29 }
30 }
31 }
32}
33
3、在設計模式實現中使用反射技術
采用反射技術可以簡化工廠的實現。
(1)工廠方法:通過反射可以將需要實現的子類名稱傳遞給工廠方法,這樣無須在子類中實現類的實例化。
(2)抽象工廠:使用反射可以減少抽象工廠的子類。
采用反射技術可以簡化工廠代碼的復雜程度,在.Net項目中,采用反射技術的工廠已經基本代替了工廠方法。
采用反射技術可以極大地簡化對象的生成,對以下設計模式的實現也有很大影響。
(1)命令模式:可以采用命令的類型名稱作為參數直接獲得命令的實例,並且可以動態執行命令。
(2)享元模式:采用反射技術實例化享元可以簡化享元工廠。
委托技術與設計模式
委托技術是.Net引入的一種重要技術,使用委托可以實現對象行為的動態綁定,從而提高設計的靈活性。
1、.Net中的委托技術
.NET運行庫支持稱為“委托”的引用類型,其作用類似於C++中的函數指針。與函數指針不同,委托實例獨立於其封裝方法的類,主要是那些方法與委托類型兼容。另外,函數指針只能引用靜態函數,而委托可以引用靜態和實例方法。委托主要用於.Net Framework中的事件處理程序和回調函數。
所有委托都從System.Delegate繼承而來並且有一個調用列表,這是在調用委托時所執行方法的一個鏈接列表。產生的委托可以用匹配的簽名引用任何方法,沒有為具有返回類型並在調用列表中包含多個方法的委托定義返回值。
可以使用的委托Cimbine及Remove方法在其調用列表中添加和移除方法。若要調用委托,可使用Invoke方法,或者使用BeginInvoke和EndInvoke方法異步調用委托。委托類的實現由運行庫提供,而不由用戶代碼提供。
委托適用於那種在某些語言中需要用函數指針來解決的情況,但是與函數指針不同,它是面向對象和類型安全的。
委托聲明定義一個類,它是從System.Delegate類派生的類。
委托實例封裝了一個調用列表,其中列出了一個或多個方法,每個方法稱為一個可調用實體。對於實例方法,可調用實體由一個實例和該實例的方法組成;對於靜態方法,可調用實體僅由一個方法組成。如果用一組合適的參數來調用一個委托實例,則該委托實例所封裝的每個可調用實體都會被調用,並且使用上述同一組參數。
委托實例的一個有用的屬性是它既不知道,也不關心其封裝方法所屬類的詳細信息,對它來說最重要的是這些方法與該委托的類型兼容。即只要方法的返回類型和參數表是相同的,則方法與委托類型兼容,方法的名稱不一定要與委托類相同。
定義和使用委托分為聲明、實例化和調用3個步驟。委托用委托聲明語法聲明,如:
delegate void myDelegate( );
聲明一個名為myDelegate的委托,它不帶參數並且不返回任何結果,如:
class Test
{
static void F( )
{
System.Console.WriteLine (“Test.F”);
}
static void Main ( )
{
myeDelegate d = new myDelegate (F);
d ( );
}
}
創建一個myDelegate實例,然後立即調用它。這樣做並沒有太大的意義,因為直接調用方法會更簡單。當涉及其匿名特性時,委托才能真正顯示出其效果,如:
void MultiCall (myDelegate d, int count ) {
for (int I = 0; I < count; I++) {
d( );
}
}
顯示一個重復調用 myDelegate的MultiCall 方法,這個方法不知道,也不必知道myDelegate的目標方法的類型、該方法具有的可訪問性或者是否為靜態。對它來說最重要的是目標方法與myDelegate兼容。
2、示例
下面的例子說明了委托的實現,代碼如下:
1using System;
2namespace DelegateExample
3{
4 public class TemplateMethod
5 {
6 public delegate float Comp(float a,float b);
7 public Comp myComp;
8 public TemplateMethod()
9 {}
10 public float DoComp(float[] f)
11 {
12 float nf = float.NaN;
13 foreach(float df in f)
14 {
15 if(float.IsNaN(nf))
16 nf = df;
17 else
18 nf = myComp(nf,df);
19 }
20 return nf;
21 }
22
23 }
24}
委托技術與GOF設計模式中委托的關系
需要指出的是,.NET中的委托技術與GOF在《設計模式》中所提列的委托的意圖一致,但在實現方法上有相當大的區別。.Net中的委托更進一步地降低了對象間的耦合性,將靜態的組合關系變為運行時的動態組合關系。
GOF在《設計模式》中定義的委托是:“委托是一種組合方法,它使組合具有與繼承同樣的復用能力。在委托方式下,有兩個對象參與處理一個請求,接受請求的對象將操作委托給它的代理者(delegate),它類似於子類將請求交給它的父類處理。使用繼承時,被繼承的操作總能引用接受請求的對象。在C++中通過this成員變量,在Smalltalk中則通過self。委托方式為了得到同樣的效果,接受請求的對象將自身傳給被委托者(代理人),使被委托的操作可以引用接受請求的對象。”
如果采用.Net的委托技術,上述結構可以更加靈活。Window不引用Rectangle即可實現Area的計算,為此首先聲明一個計算面積的委托定義,示例代碼如下:
public delegate float Darea();
然而在Window類中聲明與這個代理一致的接口:
class Window
{
public Darea Area;
}
這裡不需要引用Rectangle類,只是在執行時動態綁定即可:
Rectangle rc = new Rectangle();
Window w = new Window();
w.Area = new Darea(rc.Area);
這樣當調用w的Area時,實際調用的是Reactangel的Area方法。從實現意圖上看,.NET的委托更好地實現了GOF所闡述的意圖,結構上也更為靈活。但這兩種委托解決的不是一個層面的問題,GOF的委托強調的是一種策略,而.Net和委托技術則是具體實現。
委托技術與設計模式實現
采用委托技術可以進一步實現用組合代替繼承的思路,很多采用繼承實現的關系可以采用委托實現。采用委托可以簡化下列設計模式的使用。
(1)模板方法:這種方法采用繼承實現具體方法,采用委托可以動態實現方法的組合。
(2)觀察者:可以使用事件委托實現觀察者與主題之間的通信。
(3)中介者:使用委托可以去除工件與中介者之間的耦合關系。反射(Reflection)是.NET中的重要機制,通過放射,可以在運行時獲得.Net中每一個類型(包括類、結構、委托、接口和枚舉等)的成員,包括方法、屬性、事件,以及構造函數等。還可以獲得每個成員的名稱、限定符和參數等。有了反射,即可對每一個類型了如指掌。如果獲得了構造函數的信息,即可直接創建對象,即使這個對象的類型在編譯時還不知道。
1、.Net可執行應用程序結構
程序代碼在編譯後生成可執行的應用,我們首先要了解這種可執行應用程序的結構。
應用程序結構分為應用程序域—程序集—模塊—類型—成員幾個層次,公共語言運行庫加載器管理應用程序域,這種管理包括將每個程序集加載到相應的應用程序域以及控制每個程序集中類型層次結構的內存布局。
程序集包含模塊,而模塊包含類型,類型又包含成員,反射則提供了封裝程序集、模塊和類型的對象。我們可以使用反射動態地創建類型的實例,將類型綁定到現有對象或從現有對象中獲取類型,然後調用類型的方法或訪問其字段和屬性。反射通常具有以下用途。
(1)使用Assembly定義和加載程序集,加載在程序集清單中列出模塊,以及從此程序集中查找類型並創建該類型的實例。
(2)使用Module了解包含模塊的程序集以及模塊中的類等,還可以獲取在模塊上定義的所有全局方法或其他特定的非全局方法。
(3)使用ConstructorInfo了解構造函數的名稱、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法來調用特定的構造函數。
(4)使用MethodInfo了解方法的名稱、返回類型、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法來調用特定的方法。
(5)使用FIEdInfo了解字段的名稱、訪問修飾符(如public或private)和實現詳細信息(如static)等,並獲取或設置字段值。
(6)使用EventInfo了解事件的名稱、事件處理程序數據類型、自定義屬性、聲明類型和反射類型等,添加或移除事件處理程序。
(7)使用PropertyInfo了解屬性的名稱、數據類型、聲明類型、反射類型和只讀或可寫狀態等,獲取或設置屬性值。
(8)使用ParameterInfo了解參數的名稱、數據類型、是輸入參數還是輸出參數,以及參數在方法簽名中的位置等。
System.Reflection.Emit命名空間的類提供了一種特殊形式的反射,可以在運行時構造類型。
反射也可用於創建稱為類型浏覽器的應用程序,使用戶能夠選擇類型,然後查看有關選定類型的信息。
此外,JScript等語言編譯器使用反射來構造符號表。System.Runtime.Serialization命名空間中的類使用反射來訪問數據並確定要永久保存的字段,System.Runtime.Remoting命名空間中的類通過序列化來間接地使用反射。
2、反射技術示例
下面是反射技術的示例,我們可以在程序去得時動態實例化對象,獲得對象的屬性,並調用對象的方法。
1Namespace ReflectionExample
2{
3 class Class1
4 {
5 [STAThread]
6 static void Main (string [ ] args)
7 {
8 System.Console.WriteLine(“列出程序集中的所有類型”);
9 Assembly a = Assembly.LoadFrom (“ReflectionExample.exe”);
10 Type[ ]
mytypes = a.GetTypes( );
11
12 Foreach (Type t in mytypes)
13 {
14 System.Console.WriteLine ( t.Name );
15 }
16 System.Console.ReadLine ( );
17 System.Console.WriteLine (“列出HellWord中的所有方法” );
18 Type ht = typeof(HelloWorld);
19 MethodInfo[] mif = ht.GetMethods();
20 foreach(MethodInfo mf in mif)
21 {
22 System.Console.WriteLine(mf.Name);
23 }
24 System.Console.ReadLine();
25 System.Console.WriteLine("實例化HelloWorld,並調用SayHello方法");
26 Object obj = Activator.CreateInstance(ht);
27 string[] s = {"zhenlei"};
28 Object bojName = Activator.CreateInstance(ht,s);
29 BindingFlags flags = (BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Static|BindingFlags.Instance|BindingFlags.DeclaredOnly);
30 MethodInfo msayhello = ht.GetMethod("SayHello");
31 msayhello.Invoke(obj,null);
32 msayhello.Invoke(objName,null);
33 System.Console.ReadLine();
34 }
35 }
36}
1using System;
2namespace ReflectionExample
3{
4 public class HelloWorld
5 {
6 string myName = null;
7 public HelloWorld(string name)
8 {
9 myName = name;
10 }
11 public HelloWorld() : this(null)
12 {}
13 public string Name
14 {
15 get
16 {
17 return myName;
18 }
19 }
20 public void SayHello()
21 {
22 if(myName == null)
23 {
24 System.Console.WriteLine("Hello World");
25 }
26 else
27 {
28 System.Console.WriteLine("Hello," + myName);
29 }
30 }
31 }
32}
33
3、在設計模式實現中使用反射技術
采用反射技術可以簡化工廠的實現。
(1)工廠方法:通過反射可以將需要實現的子類名稱傳遞給工廠方法,這樣無須在子類中實現類的實例化。
(2)抽象工廠:使用反射可以減少抽象工廠的子類。
采用反射技術可以簡化工廠代碼的復雜程度,在.Net項目中,采用反射技術的工廠已經基本代替了工廠方法。
采用反射技術可以極大地簡化對象的生成,對以下設計模式的實現也有很大影響。
(1)命令模式:可以采用命令的類型名稱作為參數直接獲得命令的實例,並且可以動態執行命令。
(2)享元模式:采用反射技術實例化享元可以簡化享元工廠。
>委托技術與設計模式
委托技術是.Net引入的一種重要技術,使用委托可以實現對象行為的動態綁定,從而提高設計的靈活性。
1、.Net中的委托技術
.Net運行庫支持稱為“委托”的引用類型,其作用類似於C++中的函數指針。與函數指針不同,委托實例獨立於其封裝方法的類,主要是那些方法與委托類型兼容。
另外,函數指針只能引用靜態函數,而委托可以引用靜態和實例方法。委托主要用於.Net Framework中的事件處理程序和回調函數。
所有委托都從System.Delegate繼承而來並且有一個調用列表,這是在調用委托時所執行方法的一個鏈接列表。產生的委托可以用匹配的簽名引用任何方法,沒有為具有返回類型並在調用列表中包含多個方法的委托定義返回值。
可以使用的委托Cimbine及Remove方法在其調用列表中添加和移除方法。若要調用委托,可使用Invoke方法,或者使用BeginInvoke和EndInvoke方法異步調用委托。委托類的實現由運行庫提供,而不由用戶代碼提供。
委托適用於那種在某些語言中需要用函數指針來解決的情況,但是與函數指針不同,它是面向對象和類型安全的。
委托聲明定義一個類,它是從System.Delegate類派生的類。委托實例封裝了一個調用列表,其中列出了一個或多個方法,每個方法稱為一個可調用實體。對於實例方法,可調用實體由一個實例和該實例的方法組成;對於靜態方法,可調用實體僅由一個方法組成。如果用一組合適的參數來調用一個委托實例,則該委托實例所封裝的每個可調用實體都會被調用,並且使用上述同一組參數。
委托實例的一個有用的屬性是它既不知道,也不關心其封裝方法所屬類的詳細信息,對它來說最重要的是這些方法與該委托的類型兼容。即只要方法的返回類型和參數表是相同的,則方法與委托類型兼容,方法的名稱不一定要與委托類相同。
定義和使用委托分為聲明、實例化和調用3個步驟。委托用委托聲明語法聲明,如:
delegate void myDelegate( );
聲明一個名為myDelegate的委托,它不帶參數並且不返回任何結果,如:
class Test
{
static void F( )
{
System.Console.WriteLine (“Test.F”);
}
static void Main ( )
{
myeDelegate d = new myDelegate (F);
d ( );
}
}
創建一個myDelegate實例,然後立即調用它。這樣做並沒有太大的意義,因為直接調用方法會更簡單。當涉及其匿名特性時,委托才能真正顯示出其效果,如:
void MultiCall (myDelegate d, int count ) {
for (int I = 0; I < count; I++) {
d( );
}
}
顯示一個重復調用 myDelegate的MultiCall 方法,這個方法不知道,也不必知道myDelegate的目標方法的類型、該方法具有的可訪問性或者是否為靜態。對它來說最重要的是目標方法與myDelegate兼容。
2、示例
下面的例子說明了委托的實現,代碼如下:
1using System;
2namespace DelegateExample
3{
4 public class TemplateMethod
5 {
6 public delegate float Comp(float a,float b);
7 public Comp myComp;
8 public TemplateMethod()
9 {}
10 public float DoComp(float[] f)
11 {
12 float nf = float.NaN;
13 foreach(float df in f)
14 {
15 if(float.IsNaN(nf))
16 nf = df;
17 else
18 nf = myComp(nf,df);
19 }
20 return nf;
21 }
22
23 }
24}
委托技術與GOF設計模式中委托的關系
需要指出的是,.NET中的委托技術與GOF在《設計模式》中所提列的委托的意圖一致,但在實現方法上有相當大的區別。.Net中的委托更進一步地降低了對象間的耦合性,將靜態的組合關系變為運行時的動態組合關系。
GOF在《設計模式》中定義的委托是:“委托是一種組合方法,它使組合具有與繼承同樣的復用能力。在委托方式下,有兩個對象參與處理一個請求,接受請求的對象將操作委托給它的代理者(delegate),它類似於子類將請求交給它的父類處理。使用繼承時,被繼承的操作總能引用接受請求的對象。在C++中通過this成員變量,在Smalltalk中則通過self。
委托方式為了得到同樣的效果,接受請求的對象將自身傳給被委托者(代理人),使被委托的操作可以引用接受請求的對象。”
如果采用.Net的委托技術,上述結構可以更加靈活。Window不引用Rectangle即可實現Area的計算,為此首先聲明一個計算面積的委托定義,示例代碼如下:
public delegate float Darea();
然而在Window類中聲明與這個代理一致的接口:
class Window
{
public Darea Area;
}
這裡不需要引用Rectangle類,只是在執行時動態綁定即可:
Rectangle rc = new Rectangle();
Window w = new Window();
w.Area = new Darea(rc.Area);
這樣當調用w的Area時,實際調用的是Reactangel的Area方法。從實現意圖上看,.NET的委托更好地實現了GOF所闡述的意圖,結構上也更為靈活。但這兩種委托解決的不是一個層面的問題,GOF的委托強調的是一種策略,而.Net和委托技術則是具體實現。
委托技術與設計模式實現
采用委托技術可以進一步實現用組合代替繼承的思路,很多采用繼承實現的關系可以采用委托實現。采用委托可以簡化下列設計模式的使用。
(1)模板方法:這種方法采用繼承實現具體方法,采用委托可以動態實現方法的組合。
(2)觀察者:可以使用事件委托實現觀察者與主題之間的通信。
(3)中介者:使用委托可以去除工件與中介者之間的耦合關系。