C#特征

特征(Attribute)是C#中一种特殊的类,可以为程序集,类型,成员,参数,返回值附加数据。

特征是官方的翻译,但并不是很好。Attribute本意也是属性,但不同于类型的属性成员,这个属性是额外数据。

定义

语法

class 特征名 : 基类
{
}

基类需要是继承自System.Attribute的类。

特征名称需要以Attribute结尾。

public class DisplayAttribute : Attribute
{
    pubilc string Name {get;}

    public DisplayAttribute(string name){
        this.Name = name;
    }
}

定义一个特征用来保存说明。

一般,特征通过带参数的构造函数表示必填项,可写属性表示可选项。

特征的参数只能是基本类型,数组,和System.Type

使用特征

语法

[范围说明:特征名称(参数1,参数2,属性1=值,属性2=值)]

特征可以使用在不同的成员上,包括程序集,类,结构,枚举,委托,字段,方法,事件,索引器,参数,返回值上。

使用在部分类型上需要加应用范围说明,比如程序集assembly,返回值return

使用特征时,特征名称可以省略Attribute结尾。

参数是特征的构造函数,属性是特征的可写属性。

参数和属性值都需要是直接量。

[assembly:Description("包含坐标的程序集")]

[Description("二位坐标")]
public class Point
{
    [Description("横目标")]
    public int X {get;private set;}

    [Description("纵坐标")]
    public int Y {get;private set;}

    public Point(int x,int y){
        X=x;
        Y=y;
    }

    [return:Description("新的坐标")]
    public Point Offset(
        [Description("横坐标便宜")]int x,
        [Description("纵坐标便宜")]int y)
    {
        return new Point(X+x,Y+y);
    }
}

包含的特征成员在编译后,特征会以数据的形式保存在程序集里,要访问特征,需要使用反射。

如果特征只用来定义而不通过反射使用,那么特征就没有作用。

public static string GetDescription(Assembly assembly)
{
    object[] attributes = assembly.GetCustomAttributes(typeof(DescriptionAttribute),false);
    if(attributes.Length>0){
        return (attributes[0] as DescriptionAttribute).Name;
    }
    return null;
}

以上方法获取了程序集定义的说明特征的名称。

常用特征

.NET内部也大量使用特征,达到特定的功能。

AttributeUsageAttribute。可以使用在特征类上,用来限定特征的使用成员,以及是否可以使用多次,是否支持继承。编译器会通过此特征验证特征的使用成员是否符合。

FlagsAttribute。可以使用在枚举上,用来说明枚举是位枚举。会影响Enum.ParseEnum.ToString的结果。

ObsoluteAttribute。可以用于任何成员,说明类型已过期。编译器会通过此对过时类型进行警告或报错。

ConditionalAttribute。可以用于类和方法,说明是否编译方法调用取决于宏定义。编译器会在定义宏时才编译该特征修饰的方法。

STAThreadAttribute。用于主函数,说明主线程是单元线程。

SerializableAttribute。用于类,说明类是可序列化的。

NonSerializedAttribute。用于字段,说明不序列化该字段。

DllImportAttribute。用于方法,说明方法是平台调用。

总之特征在.NET中可以说是无处不在。

泛型特征

特征类可以定义成泛型,不过泛型特征不能在成员上使用,只能作为一般类使用。