当你创建了一个与反射相关的系统时,你应该为你自己的类型,方法,以及 属性定义一些自己的特性,这样可以让它们更容易的被访问。自定义的特性标示 了你想让这些方法在运行时如何被使用。特性可以测试一些目标对象上的属性。 测试这些属性可以最小化因为反射时可能而产生的类型错误。
假设你须 要创建一个机制,用于在运行时的软件上添加一个菜单条目到一个命令句柄上。这个须要很简单:放一个程序集到目录里,然后程序可以自己发现关于它的一些 新菜单条目以及新的菜单命令。这是利用反射可以完成的最好的工作之一:你的 主程序须要与一些还没有编写的程序集进行交互。这个新的插件同样不用描述某 个集合的功能,因为这可以很好的用接口来完成编码。
让我们为创建一 个框架的插件来开始动手写代码吧。你须要通过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.
}
}