Java与C#都从一开始就强调程序的模块化,所以写出来的程序不但包括代码逻辑,还包括类型信息等“元数据”。Java早期版本只支持有限的几种元数据,用户无法自定义新的元数据类型;后来者C#则从一开始就在支持内建attribute的同时支持用户定义的attribute,为程序在运行时提供更多信息。从Java 5开始,Java添加了一些内建元数据类型,并且开始以annotation的形式支持用户定义的元数据。这样,在Java和C#中,用户都可以通过元数据来扩展语言,为语言提供更丰富的语义。
C#里要自定义attribute类型,可以直接或间接继承 System.Attribute 类,并通过 AttributeUsageAttribute 来指定attribute的应用范围,然后像定义普通的public类一样定义attribute的内容。
指定应用范围的 AttributeTargets 有以下成员:
上面的代码光是这么写的话,_instance没人赋过值,用起来显然有问题。但我们可以写一个程序在postbuild时分析程序集,提取出其中的attribute,并且让LazyPopulateAttribute指定的属性展开为典型的double-check初始化,Instance展开后应该变为:
在运行时通过反射,PropertyInfo.GetCustomAttributes()就可以得到针对property的attribute信息。
在Java中自定义annotation类型的方法很简单,跟定义接口类似。使用@interface关键字来声明annotation类型,然后像声明方法一样声明其中的成员。与定义接口不同的是,annotation类型的成员可以带有default默认值声明。
要说C# attribute跟Java annotation有什么关系,相同点是它们都是元数据的载体,差别恐怕主要在于两者的可应用范围不同了。可以看到,两者受到C#和Java语言本身的差异的影响,可应用的范围已经有所不同,例如C#可以对程序集或者模块应用attribute,但不能对命名空间应用;Java可以对包应用annotation,但不能对例如说JAR文件之类的不属于Java语言本身所支持的组织范围应用。
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的例子:
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展开后应该变为:
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信息。
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默认值声明。
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语言本身所支持的组织范围应用。