在上面的代碼中,進行是錯誤被屏避。如果 類型名字打錯了,這個方法就找不到。就沒有方法被調用。
這還只是一 個簡單的例子。要創建一個靈活的InvokeMethod版本,須要從GetParameters() 方法上返回的參數列表中,檢測所有出現的參數類型。這樣的代碼是很沉長的, 而且很糟糕以至於我根本就不想浪費地方來演示。
反射的第三個用處就 是訪問數據成員。代碼和訪問成員函數的很類似:
// Example usage:
object field = Dispatcher.RetrieveField ( AnObject, "MyFIEld" );
// elsewhere in the dispatcher class:
public object RetrieveFIEld ( object o, string name )
{
// Find the fIEld.
FieldInfo myField = o.GetType( ).GetFIEld( name );
if ( myFIEld != null )
return myFIEld.GetValue( o );
else
return null;
}
和方法調用一樣,使用反射來取回一個數據成員,要在一個字段 上通過名字來調用類型查詢,看它是否與請求的字段名相匹配。如果發現一個, 就可以使用FIEldInfo 結構來返回值。這個構造在.Net框架裡是很常見的。數據 綁定就是利用反射來查找這些標記了綁定操作的屬性。在這種情況下,數據綁定 的動態性質超過了它的開銷。(譯注:也就是說值得使用反射進行動態綁定。)
因此,如果反射是一個如此痛苦的事情,你就須要找一個更好更簡單的 可選方案。你有三個選擇:首先就是使用接口。你可以為任何你所期望的類,結 構來定義接口(參見原則19)。這可能會使用更清楚的代碼來取代所有的反射代碼 :
IMyInterface foo = obj as IMyInterface;
if ( foo != null)
{
foo.DoWork( );
foo.Msg = "work is done.";
}
如果你用標記了特性的類廠函數來合並接 口,幾乎所有的你所期望於反射的解決方案都變得更簡單:
public class MyType : IMyInterface
{
[FactoryFunction]
public static IMyInterface
CreateInstance( )
{
return new MyType( );
}
#region IMyInterface
public string Msg
{
get
{
return _msg;
}
set
{
_msg = value;
}
}
public void DoWork( )
{
// details elided.
}
#endregion
}
把這段代碼與前面的基於反射的方案進行對 比。即使這只是簡單的例子,但還有在某些弱類型上使用所有的反射API時有精 彩之處:返回類型已經是類型化的對象。而在反射上,如果你想取得正確的類型 ,你須要強制轉換。這一操作可能失敗,而且在繼承上有危險。而在使用接口時 ,編譯器提供的強類型檢測顯得更清楚而且更易維護。
反射應該只在某 些調用目標不能清楚的用接口表示時才使用。.Net的數據綁定是在類型的任何公 共屬性上可以工作,把它限制到定義的接口上可能會很大程度上限制它的使用。 菜單句柄的例子充許任何函數(不管是實例的還是靜態的)來實現命令句柄,使用 一個接口同樣會限制這些功能只能是實例方法。FxCop 和NUnit (參見原則48)都 擴展了反射的使用,它們使用反射,是因為它們遇到的的現實的問題是最好用它 來處理的。FxCopy 檢測所有的代碼來評估它們是否與已經的原則矛盾。這須要 使用反射。NUnit 必須調用你編譯的測試代碼。它使用反射來斷定哪些你已經寫 的代碼要進行單元測試。對於你可能要寫的測試代碼,可能是一個方法集合,但 接口是不能表達它們的。NUnit使用特性來發現測試以及測試案例來讓它的工作 更簡單(參見原則42)。
當你可以使用接口策劃出你所期望調用的方法和 屬性時,你就可以擁有一個更清楚,更容易維護的系統。反射是一個在數據以後 綁定上功能強大的工具。.Net框架使用它實現對Windows控件和Web控件的數據綁 定。然而,很多常規情況下很少用,而是使用類廠,委托,以及接口來創建代碼 ,這可以產生出更容易維護的系統。
返回教程目錄