HI END


一种永不妥协,追求极致与完美的精神与态度。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

C# attribute和Java annotation(转)

Posted on 2013-03-21 08:51  HI END  阅读(936)  评论(0编辑  收藏  举报
 
呃,今天感觉就被问了一个真的细到点上的问题,但我就正好不会。我还真是没自定义过Java的annotation,虽然没少用别人定义好的。
真糟糕,赶紧简单笔记一下。至少要保持最低限度的熟练……Java啊 T T

Java与C#都从一开始就强调程序的模块化,所以写出来的程序不但包括代码逻辑,还包括类型信息等“元数据”。Java早期版本只支持有限的几种元数据,用户无法自定义新的元数据类型;后来者C#则从一开始就在支持内建attribute的同时支持用户定义的attribute,为程序在运行时提供更多信息。从Java 5开始,Java添加了一些内建元数据类型,并且开始以annotation的形式支持用户定义的元数据。这样,在Java和C#中,用户都可以通过元数据来扩展语言,为语言提供更丰富的语义。

C#里要自定义attribute类型,可以直接或间接继承System.Attribute类,并通过AttributeUsageAttribute来指定attribute的应用范围,然后像定义普通的public类一样定义attribute的内容。
指定应用范围的AttributeTargets有以下成员:
MSDN 写道
Assembly Attribute can be applied to an assembly.
Module Attribute can be applied to a module.
Class Attribute can be applied to a class.
Struct Attribute can be applied to a structure; that is, a value type.
Enum Attribute can be applied to an enumeration.
Constructor Attribute can be applied to a constructor.
Method Attribute can be applied to a method.
Property Attribute can be applied to a property.
Field Attribute can be applied to a field.
Event Attribute can be applied to an event.
Interface Attribute can be applied to an interface.
Parameter Attribute can be applied to a parameter.
Delegate Attribute can be applied to a delegate.
ReturnValue Attribute can be applied to a return value.
GenericParameter Attribute can be applied to a generic parameter.
All Attribute can be applied to any application element.

一个简单的attribute的例子:
C#代码 复制代码 收藏代码
  1. using System;   
  2.   
  3. // define a custom attribute   
  4. [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]   
  5. public class LazyPopulateAttribute : Attribute {   
  6.     Type _propType;   
  7.        
  8.     public LazyPopulateAttribute(Type propType) {   
  9.         _propType = propType;   
  10.     }   
  11.        
  12.     public Type PropertyType {   
  13.         get { return _propType; }   
  14.     }   
  15. }   
  16.   
  17.   
  18. public class Singleton {   
  19.     static Singleton _instance;   
  20.        
  21.     private Singleton() { }   
  22.        
  23.     // use the custom attribute   
  24.     [LazyPopulate(typeof(Singleton))]   
  25.     public static Singleton Instance {   
  26.         get { return _instance; }   
  27.     }   
  28. }   
  29.   
  30. static class Program {   
  31.     static void Main(string[] args) {   
  32.         var instance = Singleton.Instance;   
  33.         // ...   
  34.     }   
  35. }  
using System;

// define a custom attribute
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class LazyPopulateAttribute : Attribute {
    Type _propType;
    
    public LazyPopulateAttribute(Type propType) {
        _propType = propType;
    }
    
    public Type PropertyType {
        get { return _propType; }
    }
}


public class Singleton {
    static Singleton _instance;
    
    private Singleton() { }
    
    // use the custom attribute
    [LazyPopulate(typeof(Singleton))]
    public static Singleton Instance {
        get { return _instance; }
    }
}

static class Program {
    static void Main(string[] args) {
        var instance = Singleton.Instance;
        // ...
    }
}

上面的代码光是这么写的话,_instance没人赋过值,用起来显然有问题。但我们可以写一个程序在postbuild时分析程序集,提取出其中的attribute,并且让LazyPopulateAttribute指定的属性展开为典型的double-check初始化,Instance展开后应该变为:
C#代码 复制代码 收藏代码
  1. public static Singleton Instance {   
  2.     get {   
  3.         var instance = _instance;   
  4.         if (null == instance) {   
  5.             lock(_lockObj) { // _lockObj是应该生成的成员   
  6.                   if (null == _instance) {   
  7.                     _instance = instance = new Singleton();   
  8.                 }   
  9.             }   
  10.         }   
  11.         return instance;   
  12.     }   
  13. }  
public static Singleton Instance {
    get {
        var instance = _instance;
        if (null == instance) {
            lock(_lockObj) { // _lockObj是应该生成的成员
                  if (null == _instance) {
                    _instance = instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

在运行时通过反射,PropertyInfo.GetCustomAttributes()就可以得到针对property的attribute信息。具体要如何实现这个例子里的postbuild处理,下一篇再写。

Java方面,支持3种内建的annotation,包括@Override、@Deprecated、@SuppressWarnings。
指定应用范围的ElementType枚举类型有以下成员:
Javadoc 写道
ANNOTATION_TYPE Annotation type declaration
CONSTRUCTOR Constructor declaration
FIELD Field declaration (includes enum constants)
LOCAL_VARIABLE Local variable declaration
METHOD Method declaration
PACKAGE Package declaration
PARAMETER Parameter declaration
TYPE Class, interface (including annotation type), or enum declaration

在Java中自定义annotation类型的方法很简单,跟定义接口类似。使用@interface关键字来声明annotation类型,然后像声明方法一样声明其中的成员。与定义接口不同的是,annotation类型的成员可以带有default默认值声明。
Java代码 复制代码 收藏代码
  1. public @interface NotImplemented {   
  2.     public enum Severity { CRITICAL, HIGH, MEDIUM, LOW, NONE }   
  3.     Severity severity() default Severity.NONE;   
  4. }  
public @interface NotImplemented {
    public enum Severity { CRITICAL, HIGH, MEDIUM, LOW, NONE }
    Severity severity() default Severity.NONE;
}


要说C# attribute跟Java annotation有什么关系,相同点是它们都是元数据的载体,差别恐怕主要在于两者的可应用范围不同了。可以看到,两者受到C#和Java语言本身的差异的影响,可应用的范围已经有所不同,例如C#可以对程序集或者模块应用attribute,但不能对命名空间应用;Java可以对包应用annotation,但不能对例如说JAR文件之类的不属于Java语言本身所支持的组织范围应用。
除去语言差异的影响,Java annotation有一个显著的特点就是它可以限定在别的annotation类型的定义上应用,也就是@Target({ElementType.ATTRIBUTE})。Java的@interface声明是extends java.lang.annotation.Annotation的语法糖,所以有@Target({ElementType.TYPE})的annotation也可以应用在annotation类型的定义上。在C#中,有[AttributeUsage(AttributeTarget.Class)]的attribute可以应用在任意类的定义上,而attribute本身就是个普通的类,所以自然也可以应用。不过在C#里无法直接定义出只能作用于attribute类型定义上的attribute,也就是说没有@Target({ElementType.ATTRIBUTE})的对应物,因而无法直接定义出专用的meta-annotation。这应该算是两者最大的区别了吧?
还有一个差异:C#的attribute总是能带到运行时的;Java的annotation则可以选择RetentionPolicy,可以是CLASS、RUNTIME或者SOURCE,所以不一定会带到运行时,甚至不一定会带到编译出来的.class文件里。javac可以挂上一些钩子,可以将annotation处理直接挂在编译过程中,所以SOURCE级的annotation很有意义。Project Lombok把这种能力发挥到了极致……最近不停看到它的消息,但一直没深入调查,真是罪过啊 =v=

不知道今天被问孤城的问题这样解答算不算对呢……?
说真的,上面提到的两个差异里,第二个我是前几天才刚看了一次的,不过被问的时候没想起来 T T 而第一个差异我一直没从“限制”的角度看,如果从“所有可应用范围”来看的话,我觉得还是C#的attribute范围大些,所以也是这么回答的。仔细想想,其实很难说谁大谁小,只能说设计得比较不同……