• Effective C# Item42:利用特性简化反射


        当我们在构建基于反射的系统时,我们应该为期望使用的类型、方法和属性创建定制特性,从而简化对它们的访问。定制特性描述了我们对于“方法如何在运行时被使用”所做的期望,它可以测试目标对象的某些属性,可以将“伴随反射发生的拼写错误”的可能性降至最低。

        当我们需要通过反射来访问某种类型的对象时,需要加载某个dll文件中的程序集,然后遍历程序集中的类型,由于程序集中的类型会非常多,如何确定遍历的类型是我们需要的类型呢?如果在类型中,添加一个定制特性,那么我们可以通过对类型特性的判断,来确定这个类型是否是我们需要的类型。

        来看下面的代码。

    代码
    1 // Find all the assemblies in the Add-ins directory:
    2  string AddInsDir = string.Format( "{0}/Addins", Application.StartupPath);
    3  string[] assemblies = Directory.GetFiles( AddInsDir, "*.dll" );
    4  foreach ( string assemblyFile in assemblies )
    5 {
    6 Assembly asm = Assembly.LoadFrom( assemblyFile );
    7 // Find and install command handlers from the assembly.
    8   foreach( System.Type t in asm.GetExportedTypes( ))
    9 {
    10 if (t.GetCustomAttributes(
    11 typeof( CommandHandlerAttribute ), false ).Length > 0 )
    12 {
    13 // Found the command handler attribute on this type.
    14 // This type implements a command handler.
    15 // configure and add it.
    16   }
    17 // Else, not a command handler. Skip it.
    18   }
    19 }

        上面代码中说明,如果指定类型中包含CommandHandlerAttribute特性,那么这个类型就是我们期望的类型。

        定制特性除了可以标识期望类型外,它还有其他作用,我们可以利用特性构造函数来设置业务中使用的一些数据,虽然这样做有时不是很合适。

        来看以下的代码。

    代码
    1 [AttributeUsage( AttributeTargets.Property ) ]
    2  public class DynamicMenuAttribute : System.Attribute
    3 {
    4 private string _menuText;
    5 private string _parentText;
    6
    7 public DynamicMenuAttribute( string CommandText,
    8 string ParentText )
    9 {
    10 _menuText = CommandText;
    11 _parentText = ParentText;
    12 }
    13
    14 public string MenuText
    15 {
    16 get { return _menuText; }
    17 set { _menuText = value; }
    18 }
    19
    20 public string ParentText
    21 {
    22 get { return _parentText; }
    23 set { _parentText = value; }
    24 }
    25 }

        上面代码定义了一个定制特性,其中包含了两个属性,下面的代码说明了如何使用这个特性。

    代码
    1 // Find the types in the assembly
    2  foreach( Type t in asm.GetExportedTypes( ) )
    3 {
    4 if (t.GetCustomAttributes(
    5 typeof( CommandHandlerAttribute ), false).Length > 0 )
    6 {
    7 // Found a command handler type:
    8   ConstructorInfo ci =
    9 t.GetConstructor( new Type[0] );
    10 if ( ci == null ) // No default ctor
    11 continue;
    12 object obj = ci.Invoke( null );
    13 PropertyInfo [] pi = t.GetProperties( );
    14
    15 // Find the properties that are command
    16 // handlers
    17 foreach( PropertyInfo p in pi )
    18 {
    19 string menuTxt = "";
    20 string parentTxt = "";
    21 object [] attrs = p.GetCustomAttributes(
    22 typeof ( DynamicMenuAttribute ), false );
    23 foreach ( Attribute at in attrs )
    24 {
    25 DynamicMenuAttribute dym = at as
    26 DynamicMenuAttribute;
    27 if ( dym != null )
    28 {
    29 // This is a command handler.
    30 menuTxt = dym.MenuText;
    31 parentTxt = dym.ParentText;
    32 MethodInfo mi = p.GetGetMethod();
    33 EventHandler h = mi.Invoke( obj, null )
    34 as EventHandler;
    35 UpdateMenu( parentTxt, menuTxt, h );
    36 }
    37 }
    38 }
    39 }
    40 }
    41
    42 private void UpdateMenu( string parentTxt, string txt,
    43 EventHandler cmdHandler )
    44 {
    45 MenuItem menuItemDynamic = new MenuItem();
    46 menuItemDynamic.Index = 0;
    47 menuItemDynamic.Text = txt;
    48 menuItemDynamic.Click += cmdHandler;
    49
    50 //Find the parent menu item.
    51 foreach ( MenuItem parent in mainMenu.MenuItems )
    52 {
    53 if ( parent.Text == parentTxt )
    54 {
    55 parent.MenuItems.Add( menuItemDynamic );
    56 return;
    57 }
    58 }
    59 // Existing parent not found:
    60 MenuItem newDropDown = new MenuItem();
    61 newDropDown.Text = parentTxt;
    62 mainMenu.MenuItems.Add( newDropDown );
    63 newDropDown.MenuItems.Add( menuItemDynamic );
    64 }

        一般来说,是不会在定制特性中包含业务数据的,上述代码只是说明的作用。

        特性实际上声明的是我们在运行时的意图,在一个元素上标记特性可以描述它的用途,以及降低在运行时查找该元素的难度,如果没有特性,我们就需要定义某种命名约定,来支持在运行时查找类型及其元素,命名约定是错误的源头,用特性来表达意图可以将更多的责任由开发人员转移到编译器上。

        我们通常使用反射来创建可以被重新配置的动态代码。通过设计和实现特性类,强制开发人员用它们来声明可以被动态使用的类型、方法和属性,可以减少应用程序的运行时错误。

  • 相关阅读:
    centos7 修改中文字符集
    Can't locate Data/Dumper.pm in perl5的处理
    MySQL crash-safe replication(3): MySQL的Crash Safe和Binlog的关系
    MySQL crash-safe replication(2):
    MySQL crash-safe replication(1)
    《Linux性能调优指南 》全书
    Oracle Database 12c Preinstall Steps for Oracle Linux Simplified
    MySQL的binlog2sql闪回
    Python pip 安装与使用
    LSM树由来、设计思想以及应用到HBase的索引
  • 原文地址:https://www.cnblogs.com/wing011203/p/1675146.html
Copyright © 2020-2023  润新知