读书笔记 黄忠诚 标签

 

2-2-1  Attributes语义学

    程序语言历经多次革命,多重继承已渐渐消失于近代程序语言中,其空下的位置也正式由接口(Interface)所取代。另一方面,对象持久机制也慢慢成为程序语言的基本建设之一,不过目前出现一个诡谲的现象,在Java语言中,类要支持Serialization就必须声明成如程序2-8的形式。

程序2-8

public class ClassA implements    java.io.Serializable

 

    有趣的是,Serializable界面中并未声明任何须实现的函数,纯粹只是个空接口,这一点似乎不太符合接口的正常用法。的确,Serializable被当成是识别证使用,.NET Framework中此类运用被明确分割成另一种机制,它的名称叫AttributeAttribute的出现是为了由接口或抽象类中分离出标签类型的运用,例如java.io.Serializable是一个接口,但其真正的用途却是为此类贴上一个标签,代表着此类支持Serializable能力。Serializable界面在.NET Framework则被正名为SerializableAttribute相较两者,.NET Framework在语意上似乎显得明确许多。笔者喜欢将Attribute称之为标签(便利贴,如果你觉得亲切的话),因为它可以贴在类、方法、属性、成员变量之上,告知处理者应该如何处理目标物。此处须特别澄清一点,Attribute本身是被动物,简单地说是其自身不可能知道被贴在哪一个目标上,所以在Attribute中是无法存取目标物的。

 

 

2-2-2  看看Attributes能做什么

    用户预定义的Attribute必须继承自System.Attribute,其构造函数所接受的参数代表着使用该Attribute时所须传入的参数,程序2-9是一个最简单的Attribute范例。

程序2-9  简单的Attribute范例

using System;

using System.ComponentModel;

namespace AttributeFullDemo

{

  [AttributeUsage(AttributeTargets.Class,

                     Inherited = false,

                     AllowMultiple = false)]

  public class ClassShowProcessAttribute:Attribute

  {

    private bool _show = false;

    public bool Show

    {

      get

      {

        return _show;

      }

    }

 

    public ClassShowProcessAttribute(bool show)

    {

       _show = show;

    }

  }

}

 

    有趣的是用户预定义的Attribute本身也贴上了另一个Attribute,不过这在语意上并不冲突,AttributeUsage表着此Attribute所适用的情况,其第一个参数是该Attribute所适用的范围,其可以是类、方法、属性等等;第二个参数代表着当Attribute被贴在目标物上后,继承至目标物的继承者是否会连此Attribute一并继承;第三个参数代表此Attribute是否允许重复贴在同一目标物上,意思是说是否允许同时贴两个以上同样的Attribute在目标物上。基本上Attribute是绑定于Type上,设计人员得通过Type.GetCustomAttributes函数来判定此Type中拥有哪些Attribute,程序2-102-11是使用ClassShowProcessAttribute的范例。

 

程序2-10  贴上ClassShowProcessAttribute的类范例

using System;

namespace AttributeFullDemo

{

  [ClassShowProcess(true)]

  public class AttrClassTest

  {

    public AttrClassTest(){}

  }

}

 

程序2-11  使用贴上ClassShowProcessAttribute类的范例

private void btnTestClass_Click(object sender, System.EventArgs e)

{

   object[] o = new object[]{new AttrClassTest(),

                                  new AttrClassTest(),"TEST"};

   foreach(object item in o)

   {

     ClassShowProcessAttribute[] attr =

          (ClassShowProcessAttribute[])item.GetType().GetCustomAttributes(

                                   typeof(ClassShowProcessAttribute),false);

     if(attr.Length != 0)

       if(attr[0].Show)

         textBox1.Text += "\r\n"+item.ToString();

   }

}

 

    程序2-11中创建了一个object类型的数组,在其中放入了两个AttrClassTest对象及一个string对象,接着遍历此对象数组一一调用Type.GetCustomAttributes函数,GetCustomAttributes函数拥有两个重载函数,其中之一接受inherited参数,此参数对应至AttributeUsageinherited参数。另一个则接受inherited及一个Type参数,此处使用的是第二个重载函数,并指定其TypeClassShowProcessAttribute这可以使返回值仅限于此Attribute。此处有一点须特别注意,Attribute对象的创建时机是设计者调用GetCustomAttributes函数时其拥有的两个重载函数代表两种创建方式,第一种方式会创建该Type的所有Attributes对象,第二种则只创建与调用时所传入Type参数相符的Attribute对象,除非有特殊需求,否则设计人员应该尽量使用第二种,减少创建无意义的Attribute对象。

 

2-2-3  Attribute in Member Field

    除了绑定至类外,Attribute也可以绑定至成员变量上,程序2-12、程序2-13展现了此种应用。

程序2-12  可绑定至成员变量上的Attribute范例

using System;

using System.ComponentModel;

using System.Reflection;

namespace AttributeFullDemo

{

  public interface IFieldShow

  {

    string Show(object o);

  }

  [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property,

                     Inherited = false,

                     AllowMultiple = false)]

  public class FieldShowAttribute:Attribute

  {

    private bool _show = false;

    private Type _showClass = null;

 

    public bool Show

    {

      get

      {

        return _show;

      }

    }

    public Type ShowClass

  {

    get

    {

      return _showClass;

    }

  }

 

  public IFieldShow CreateShowInstance()

  {

    object o;

    ConstructorInfo cinfo = _showClass.GetConstructor(new Type[]{});

    o = cinfo.Invoke(null);

    return (IFieldShow)o;

  }

 

  public FieldShowAttribute(bool show,Type showClass)

  {

    _show = show;

    _showClass = showClass;

    if(_showClass == null)

      throw new Exception("must provides showClass!!");

  }

 }

}

 

程序2-13  贴上FieldShowAttribute的类范例

using System;

using System.ComponentModel;

 

namespace AttributeFullDemo

{

  public class StringFieldShow:IFieldShow

  {

     string IFieldShow.Show(object o)

     {

       return o.ToString();

     }

  }

 

  public class AttrFieldTest

  {

     [FieldShow(true,typeof(StringFieldShow))]

     public int _number = 0;

 

     public AttrFieldTest()

     {

     }

  }

}

 

    此程序也展现了另一种Attribute类型的应用,它接受一个Type数,这代表着设计人员于使用此Attribute时可以指定一个自定义Type改变其显示的行为,设计人员也可以提供一系列的预先定义的Type供用户运用,这将Attribute带往另一个较为复杂且高扩展性的应用面,程序2-14的范例展示了这一点。

程序2-14  使用贴上FieldShowAttribute类的范

private void btnTestField_Click(object sender, System.EventArgs e)

{

  AttrFieldTest[] o = new AttrFieldTest[]{new AttrFieldTest(),

                                                  new AttrFieldTest()};

  o[0]._number = 15;

  o[1]._number = 30;

  foreach(AttrFieldTest item in o)

  {

     foreach(FieldInfo field in item.GetType().GetFields())

     {

        FieldShowAttribute[] fattr =

                          (FieldShowAttribute[])field.GetCustomAttributes(

typeof(FieldShowAttribute),false);

        if(fattr.Length != 0)

        {

          textBox1.Text += "\r\n"+field.Name+

                              " ="+

                              fattr[0].CreateShowInstance().Show(

                                                         field.GetValue(item));

     }

  }

}

 

 这种概念是将原本应该由使用端处理的工作交由另一个类来处理,这可以有效地切割应用程序,给予较高的扩展性及明确的定位。不过此范例并不是一个良好的设计,因为其在寻找Attribute时会为每一个Attribute.Type创建一个实体,因而有滥用内存之嫌。要改善这一点,设计人员可以于Attribute中预定义一个静态函数,并要求传入的Attribute.Type型必须拥有一个静态函数,程序2-15、程序2-16是修改后的范例。

程序2-15  修改后的FieldShowAttribute范例

using System;

using System.ComponentModel;

using System.Reflection;

 

namespace AttributeFullDemo

{

   public interface IFieldShow

   {

     string Show(object o);

   }

 

   [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property,

                      Inherited = false,

                      AllowMultiple = false)]

   public class FieldShowAttribute:Attribute

   {

     private bool _show = false;

     private Type _showClass = null;

     public bool Show

     {

        get

        {

          return _show;

        }

     }

 

     public Type ShowClass

     {

       get

       {

         return _showClass;

       }

     }

 

     public IFieldShow CreateShowInstance()

     {

       object o;

       ConstructorInfo cinfo = _showClass.GetConstructor(new Type[]{});

       o = cinfo.Invoke(null);

       return (IFieldShow)o;

     }

 

public static string ShowValue(FieldShowAttribute attr,

object o,FieldInfo valueObject)

     {

       MethodInfo mi= attr.ShowClass.GetMethod(

                                      "Show",

                            BindingFlags.Static|BindingFlags.Public);

       return(string)mi.Invoke(null,new object[]{valueObject.GetValue(o)});

     }

 

     public FieldShowAttribute(bool show,Type showClass)

     {

       _show = show;

       _showClass = showClass;

       if(_showClass == null)

          throw new Exception("must provides showClass!!");

     }

   }

}

 

程序2-16  修改后贴上FieldShowAttribute类的范例

using System;

using System.ComponentModel;

 

namespace AttributeFullDemo

{

    public class StringFieldShow:IFieldShow

    {

      string IFieldShow.Show(object o)

      {

         return o.ToString();

      }

    }

 

    public class StringFieldShow2

    {

      public static string Show(object o)

      {

        return o.ToString();

      }

    }

 

    public class AttrFieldTest

    {

      [FieldShow(true,typeof(StringFieldShow2))]

      public int _number = 0;

      public AttrFieldTest(){}

    }

}

 

    此范例使用了静态函数及Reflection技术来实现前面所提及的想法,现在使用端的范例就不需要为每一个Attribute创建一个ShowClass对象了,见程序2-17

程序2-17  使用贴上FieldShowAttribute类的范

private void btnTestField_Click(object sender, System.EventArgs e)

{

  AttrFieldTest[] o = new AttrFieldTest[]{new AttrFieldTest(),

                                                  new AttrFieldTest()};

  o[0]._number = 15;

  o[1]._number = 30;

  foreach(AttrFieldTest item in o)

  {

     foreach(FieldInfo field in item.GetType().GetFields())

     {

       FieldShowAttribute[] fattr =

                         (FieldShowAttribute[])field.GetCustomAttributes(

typeof(FieldShowAttribute),false);

       if(fattr.Length != 0)

          textBox1.Text+="\r\n"+ FieldShowAttribute.ShowValue(fattr[0],item,field);

      }

  }

}

 

posted on 2008-08-25 21:15  simhare  阅读(228)  评论(0)    收藏  举报

导航