在说自定义之前,有必要先介绍一些基本的概念.
元数据:就是C#中封装的一些类,无法修改,类成员的特性被称为元数据中的注释
1.什么是特性?
(1)属性和特性的区别
属性:属性是面向对象思想里所说的封装在类里面的数据字段,Get,Set方法.
特性:就相当于类的元数据.
来看看官方解释?
特性是给指定的某一声明的一则附加的声明性信息。 允许类似关键字的描述声明。它对程序中的元素进行标注,如类型、字段、方法、属性等。从.net角度看,特性是一种 类,这些类继承于System.Attribute类,用于对类、属性、方法、事件等进行描述,主要用在反射中。但从面向对象的级别看,其实Attribute是类型级别的,而不是对象级别。
Attribute和.net文件的元数据保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响程序的行为.
2.特性的应用
(1).net中特性用来处理多种问题,比如序列化,程序的安全特性,防止即时编译器对程序代码进行优化从而代码容易调试等等.定值特性的本质上是在一个类的元素上添加附加信息,并在运行其通过反射得带该附加信息(在使用数据实体对象时经常用到)
(2)Attribute作为编译期的指令时的应用
Conditional起条件编译的作用,只有满足条件,才允许编译器对它的代码进行编译.一般在程序调试的时候使用.
DllImport:用来标记非.net函数,表明该方法在一个外部的DLL定义.
Obsolete:这个属性用来标记当前的方法已被废弃,不再使用.
注意:Attribute是一个类,因此DllImport也是一个类,Attribute类是在编译的时候实例化,而不是想通常那样在运行时实例化.
CLSCompliant:保证整个程序集代码遵守CLS,否则编译将报错.
3.自定义特性
使用AttributeUsage,来控制如何应用新定义的特性.
[AttributeUsageAttribute(AttributeTargets.All 可以应用到任何元素
,AllowMultiple=true, 允许应用多次,我们的定值特性能否被重复放在同一个程序实体前多次。
,Inherited=false,不继承到派生
)]
特性也是一个类,必须继承于System.Attribute类,命名规范我类名+Attribute.不管直接还是间接继承,都会成为一个特性类,特性类的声明定义了一种可以放置在声明之上新的特性.使用[]语法使用自定义特性,可以使用反射来查看自定义特性.
案例:
public class MyselfAttribute:System.Attribute
不过说实话,特性确实常用到,但是自定义特性几乎用不到,貌似老外喜欢用.
如果不能自己定义一个特性并使用它,我姓你肯定觉得我在忽悠你,假设我们有这样一个很常见的需求:我们在创建或者更新一个类文件时,需要说要这个类时什么时候,由谁创建的,在一行的更新中还要说明在什么时候由谁更新的,可以记录也可以不记录更新的内容,以往的情况你会怎么做?肯定想到了添加注释:
//更新:张三,2015-2-3,修改了ToString()方法
//更新:李四,2014-4-5
//创建:王五,2011-7-8
public class DemoClass
{
//dosomething
}
这样做没问题,想要啥,就写啥,看起来很好啊,借用金星老师的一句话就是完美!
but,如果我们有一天想把这些记录保存到数据库中作为备份呢?你是不是要一条一条的去查看源文件,找出注释,然后在一条条的插入到数据库中呢?
通过上面特性的定义,我们知道特性可以用来给类型添加元数据(描述书觉得数据,包括数据是否被修改,何时创建,创建人,这些数据可以是一个类,方法,属性),这些元数据可以用于描述类型.那么在此处,特性就会派上用场.那么在本例中,元数据应该是:注释类型(更行或者创建),修改人,日期,备注信息(可有可无).而特性的目标类型是DemoClass类.
按照对于附加到DemoClass类上的元数据的理解,我们先创建一个封装了元数据的类:
public class RecordAttribute
{
private string recordType;//记录类型:更新或者创建
private string author;//作者
private DateTime data;//日期
private string memo;//备注
//构造函数的参数在特性中也称为"位置参数"
public RecordAttribute(string recordType,string author,string date)
{
this.recordType = recordType;
this.author = author;
this.data = Convert.ToDateTime(date);
}
//对于位置参数,通常只提供get访问
public string RecordType { get { return recordType; } }
public string Author { get { return author; } }
public DateTime Date { get { return Date; } }
//构建一个属性,在特性中也叫"命名参数"
public string Memo {
get { return memo; }
set { memo = value; }
}
}
注意:构造函数的参数date,必须为一个常量,Type类型,或者是常量数组,所以不能直接传递DateTime类型.