Effective C# 避免過度使用反射

來源:互聯網
上載者:User

建立二進位的組件時,同時也意味著你要使用遲後綁定和反射來尋找你所須要的具有特殊功能代碼。反射是一個很有力的工具,而且它讓你可以寫出可動態配置的軟體。使用反射,一個應用程式可以通過添加新的組件來更新功能,而這些組件是在軟體最開始發布時沒有的。這是有利的。
        這一靈活性也帶來了一些複雜的問題,而且複雜問題的增加又會增加出現其它問題的可能。當你使用反射時,你是圍繞著C#的安全類型。然而,成員調用的參數和傳回值是以System.Object類型存在的。你必須在運行時確保這些類型是正確的。簡單的說,使用反射可以讓建立動態程式變得很容易,但同時也讓程式出現錯誤變得很容易。通常,簡單的思考一下,你就可以通過建立一系列介面集合來最小化或者移除反射,而這些介面集合應該表達你對類型的假設。

        反射給了你建立類型執行個體的功能,以及在對象上調用成員方法,以及訪問對象上的成員資料。這聽上去就跟每天的編程任務是一樣的。確實是這樣的,對於反射,並沒有什麼新奇的:它就是動態建立其它的二進位組件。大多數情況下,你並不須要像反射這樣的靈活性,因為有其它可選的更易維護的方案。

        讓我們從建立一個給定類型的執行個體開始,你可以經常使用一個類廠來完成同樣的任務。考慮下面的代碼,它通過使用反射,調用預設的建構函式建立了一個MyType 的執行個體:

 

// Usage:Create a new object using reflection:
Type t = typeof( MyType );
MyType obj = NewInstance( t ) as MyType;


// Example factory function, based on Reflection:
object NewInstance( Type t )
{
// Find the default constructor:
ConstructorInfo ci = t.GetConstructor( new Type[ 0 ] );
if ( ci != null )
    // Invoke default constructor, and return
    // the new object.
    return ci.Invoke( null );

// If it failed, return null.
return null;
}

 

碼通過反射檢測了類型,而且調用了預設的建構函式來建立了一個對象。如果你須要在運行時建立一個預先不知道任何資訊的類型執行個體,這是唯一的選擇。這是一段脆弱的代碼,它依懶於預設的建構函式的存在。而且在你移除了MyType類型的預設建構函式時仍然是可以通過編譯的。你必須在運行時完成檢測,而且捕獲任何可能出現的異常。一個完成同樣功能的類廠函數,在建構函式被移除時是不能通過編譯的:

  public MyType NewInstance( )
{
return new MyType();
}

我們應該使用靜態類工廠函數來取代依懶於反射的執行個體建立方法。如果你須要執行個體對象使用遲後資料繫結,那麼應該使用類廠函數,而且使用相關的特性來標記它們。

        反射的另一個潛在的用處就是訪問類型的成員。你可以使用成員名和類型在運行時來調用實際的函數:

 

 

// Example usage:
Dispatcher.InvokeMethod( AnObject, "MyHelperFunc" );

// Dispatcher Invoke Method:
public void InvokeMethod ( object o, string name )
{
// Find the member functions with that name.
MemberInfo[] myMembers = o.GetType( ).GetMember( name );
foreach( MethodInfo m in myMembers )
{
    // Make sure the parameter list matches:
    if ( m.GetParameters( ).Length == 0 )
      // Invoke:
    m.Invoke( o, null );
}
}

 

        在上面的代碼中,進行是錯誤被屏避。如果類型名字打錯了,這個方法就找不到。就沒有方法被調用。

        這還只是一個簡單的例子。要建立一個靈活的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架構裡是很常見的。資料繫結就是利用反射來尋找這些標記了綁定操作的屬性。在這種情況下,資料繫結的動態性質超過了它的開銷。(譯註:也就是說值得使用反射進行動態綁定。)

 

因此,如果反射是一個如此痛苦的事情,你就須要找一個更好更簡單的可選方案。你有三個選擇:首先就是使用介面。你可以為任何你所期望的類,結構來定義介面。這可能會使用更清楚的代碼來取代所有的反射代碼:

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( );
}

IMyInterface#region IMyInterface
public string Msg
{
    get
    {
      return _msg;
    }
    set
    {
      _msg = value;
    }
}
public void DoWork( )
{
    // details elided.
}
#endregion
}

 

 

把這段代碼與前面的基於反射的方案進行對比。即使這隻是簡單的例子,但還有在某些弱類型上使用所有的反射API時有精彩之處:傳回型別已經是類型化的對象。而在反射上,如果你想取得正確的類型,你須要強制轉換。這一操作可能失敗,而且在繼承上有危險。而在使用介面時,編譯器提供的強型別檢測顯得更清楚而且更易維護。

        反射應該只在某些調用目標不能清楚的用介面表示時才使用。.Net的資料繫結是在類型的任何公用屬性上可以工作,把它限制到定義的介面上可能會很大程度上限制它的使用。菜單控制代碼的例子充許任何函數(不管是執行個體的還是靜態)來實現命令控制代碼,使用一個介面同樣會限制這些功能只能是執行個體方法。FxCop 和NUnit 都擴充了反射的使用,它們使用反射,是因為它們遇到的的現實的問題是最好用它來處理的。FxCopy 檢測所有的代碼來評估它們是否與已經的原則矛盾。這須要使用反射。NUnit 必須調用你編譯的測試代碼。它使用反射來斷定哪些你已經寫的代碼要進行單元測試。對於你可能要寫的測試代碼,可能是一個方法集合,但介面是不能表達它們的。NUnit使用特性來發現測試以及測試案例來讓它的工作更簡單。

        綜上所述,當你可以使用介面描述出你所期望調用的方法和屬性時,你就可以擁有一個更清楚,更容易維護的系統。反射是一個在晚綁定的功能強大的工具。.Net架構使用它實現對Windows控制項和Web控制項的資料繫結。然而,在很多不是很通用的情況下,使用類廠,委託,以及介面來建立代碼可以產生出更容易維護的系統。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.