當你創建了一個與反射相關的系統時,你應該為你自己的類型,方法,以及 屬性定義一些自己的特性,這樣可以讓它們更容易的被訪問。自定義的特性標示 了你想讓這些方法在運行時如何被使用。特性可以測試一些目標對象上的屬性。 測試這些屬性可以最小化因為反射時可能而產生的類型錯誤。
假設你須 要創建一個機制,用於在運行時的軟件上添加一個菜單條目到一個命令句柄上。這個須要很簡單:放一個程序集到目錄裡,然後程序可以自己發現關於它的一些 新菜單條目以及新的菜單命令。這是利用反射可以完成的最好的工作之一:你的 主程序須要與一些還沒有編寫的程序集進行交互。這個新的插件同樣不用描述某 個集合的功能,因為這可以很好的用接口來完成編碼。
讓我們為創建一 個框架的插件來開始動手寫代碼吧。你須要通過Assembly.LoadFrom() 函數來加 載一個程序,而且要找到這個可能提供菜單句柄的類型。然後須要創建這個類型 的一個實例對象。接著還要找到這個實例對象上可以與菜單命令事件句柄的申明 相匹配的方法。完成這些任務之後,你還須要計算在菜單的什麼地方添加文字,以及什麼文字。
特性讓所有的這些任務變得很簡單。通過用自己定義的 特性來標記不同的類以及事件句柄,你可以很簡單的完成這些任務:發現並安裝 這些潛在的命令句柄。你可以使用特性與反射來協作,最小化一些在原則43中描 述的危險事情。
第一個任務就是寫代碼,發現以及加載插件程序集。假 設這個插件在主執行程序所在目錄的子目錄中。查找和加載這個程序集的代碼很 簡單:
// Find all the assemblIEs in the Add-ins directory:
string AddInsDir = string.Format( " {0}/Addins",
Application.StartupPath );
string[] assemblIEs = Directory.GetFiles( AddInsDir, "*.dll" );
foreach ( string assemblyFile in assemblIEs )
{
Assembly asm = Assembly.LoadFrom( assemblyFile );
// Find and install command handlers from the assembly.
}
接下來,你須要 把上面最後一行的注釋替換成代碼,這些代碼要查找那些實現了命令句柄的類並 且要安裝這些句柄。加載完全程序集之後,你就可以使用反射來查找程序集上所 有暴露出來的類型,使用特性來標識出哪些暴露出來的類型包含命令句柄,以及 哪些是命令句柄的方法。下面是一個添加了特性的類,即標記了命令句柄類型:
// Define the Command Handler Custom Attribute:
[AttributeUsage( AttributeTargets.Class )]
public class CommandHandlerAttribute : Attribute
{
public CommandHandlerAttribute( )
{
}
}
這個 特性就是你須要為每個命令標記的所有代碼。總是用AttributeUsage 特性標記 一個特性類,這就是告訴其它程序以及編譯器,在哪些地方這個特性可以使用。 前面這個例子表示CommandHandlerAttribute只能在類上使用,它不能應用在其 它語言的元素上。
你可以調用GetCustomAttributes來斷定某個類是否具 有CommandHandlerAttribute特性。只有具有該特性的類型才是插件的候選類型 :
// Find all the assemblIEs in the Add-ins directory:
string AddInsDir = string.Format( "{0}/Addins", Application.StartupPath);
string[] assemblIEs = Directory.GetFiles( AddInsDir, "*.dll" );
foreach ( string assemblyFile in assemblIEs )
{
Assembly asm = Assembly.LoadFrom( assemblyFile );
// Find and install command handlers from the assembly.
foreach( System.Type t in asm.GetExportedTypes( ))
{
if (t.GetCustomAttributes (
typeof( CommandHandlerAttribute ), false ).Length > 0 )
{
// Found the command handler attribute on this type.
// This type implements a command handler.
// configure and add it.
}
// Else, not a command handler. Skip it.
}
}