1 反射技術與設計模式
反射(Reflection)是。NET中的重要機制,通過放射,可以在運作時獲得。NET中每一個類型(包括類、結構、委托、接口和枚舉等)的成員,包括方法、屬性、事件,以及構造函數等。還可以獲得每個成員的名稱、限定符和參數等。有了反射,即可對每一個類型了如指掌。如果獲得了構造函數的資訊,即可直接建立對象,即使這個對象的類型在編譯時還不知道。
1.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命名空間中的類通過序列化來間接地使用反射。
1.2 反射技術示例
下面是反射技術的示例,我們可以在程式去得時動态執行個體化對象,獲得對象的屬性,并調用對象的方法。
1
namespace 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
34
}
35
}
36
}
37
using System;
namespace ReflectionExample
public class HelloWorld
string myName = null;
public HelloWorld(string name)
myName = name;
public HelloWorld() : this(null)
{}
public string Name
get
return myName;
public void SayHello()
if(myName == null)
System.Console.WriteLine("Hello World");
else
System.Console.WriteLine("Hello," + myName);
1.3 在設計模式實作中使用反射技術
采用反射技術可以簡化工廠的實作。
(1)工廠方法:通過反射可以将需要實作的子類名稱傳遞給工廠方法,這樣無須在子類中實作類的執行個體化。
(2)抽象工廠:使用反射可以減少抽象工廠的子類。
采用反射技術可以簡化工廠代碼的複雜程度,在。NET項目中,采用反射技術的工廠已經基本代替了工廠方法。
采用反射技術可以極大地簡化對象的生成,對以下設計模式的實作也有很大影響。
(1)指令模式:可以采用指令的類型名稱作為參數直接獲得指令的執行個體,并且可以動态執行指令。
(2)享元模式:采用反射技術執行個體化享元可以簡化享元工廠。
2 委托技術與設計模式
委托技術是。NET引入的一種重要技術,使用委托可以實作對象行為的動态綁定,進而提高設計的靈活性。
2.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.2示例
下面的例子說明了委托的實作,代碼如下:
namespace DelegateExample
{
public class TemplateMethod
public delegate float Comp(float a,float b);
public Comp myComp;
public TemplateMethod()
{}
public float DoComp(float[] f)
float nf = float.NaN;
foreach(float df in f)
if(float.IsNaN(nf))
nf = df;
else
nf = myComp(nf,df);
}
return nf;
}
}
2.3 委托技術與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和委托技術則是具體實作。
2.4 委托技術與設計模式實作
采用委托技術可以進一步實作用組合代替繼承的思路,很多采用繼承實作的關系可以采用委托實作。采用委托可以簡化下列設計模式的使用。
(1)模闆方法:這種方法采用繼承實作具體方法,采用委托可以動态實作方法的組合。
(2)觀察者:可以使用事件委托實作觀察者與主題之間的通信。
(3)中介者:使用委托可以去除工件與中介者之間的耦合關系。