元数据,就是C#中封装的一些类,没法修改.类成员的特性被称为元数据中的注释.html
一、什么是特性c#
1)属性与特性的区别数组
属性(Property):属性是面向对象思想里所说的封装在类里面的数据字段,Get,Set方法。安全
特性(Attribute): 官方解释:特性是给指定的某一声明的一则附加的声明性信息。 容许相似关键字的描述声明。它对程序中的元素进行标注,如类型、字段、方法、属性等。从.net角度看,特性是一种 类,这些类继承于System.Attribute类,用于对类、属性、方法、事件等进行描述,主要用在反射中。但从面向对象的级别看,其实Attribute是类型级别的,而不是对象级别。网络
Attributes和.net文件的元素据保存在一块儿,能够用来向运行时描述你的代码,或者在程序运行的时候影响程序的行为。ide
二、特性的应用函数
(1).net中特性用来处理多种问题,好比序列化、程序的安全特性、防止即时编译器对程序代码进行优化从而代码容易调试等等。性能
定植特性的本质上是一个类的元素上去添加附加信息,并在运行其经过反射获得该附加信息(在使用数据实体对象时常常用到)学习
(2)Attribute 做为编译器的指令时的应用优化
Conditional:起条件编译的做用,只有知足条件,才容许编译器对它的代码进行编译。通常在程序调试的时候使用
DllImport: 用来标记费.net的函数,代表该方法在一个外部的DLL中定义。
Obsolete: 这个属性用来标记当前的方法已经废弃,再也不使用
注:Attribute是一个类,所以DllImport也是一个类,Attribute类是在编译的时候实例化,而不是像一般那样在运行时实例化。
CLSCompliant: 保证整个程序集代码遵照CLS,不然编译将报错。
三、自定义特性
使用AttributeUsage,来控制如何应用新定义的特性
[AttributeUsageAttribute(AttributeTargets.All 能够应用到任何元素
,AllowMultiple=true, 容许应用屡次,咱们的定值特性可否被重复放在同一个程序实体前屡次。
,Inherited=false,不继承到派生
)]
特性也是一个类,必须继承于System.Attribute类,命名规范为“类名”+Attribute。不论是直接仍是间接继承,都会成为一个特性类,特性类的声明定义了一种能够放置在声明之上新的特性。
代码以下:
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; namespace CollectionsApplication { //1. 自定义特定: //限定特性类的应用范围 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, AllowMultiple = true, Inherited = false)] //定制MsgAttribute特性类,继承于Attribute public class ClassMsgAttribute : Attribute { //定义_msg字段和Msg属性//Msg属性用于读写msg字段 string _msg; public string Msg { get { return _msg; } set { _msg = value; } } public ClassMsgAttribute() { } //重载构造函数接收一个参数,赋值给_msg字段 public ClassMsgAttribute(string s) { _msg = s; } } //2. 调用: //在Person类上标记ClassMsg特性 [ClassMsg(Msg = "this is the class for name attribute")] class Person { //在_name字段上应用ClassMsg特性 [ClassMsg("this is name attribute.")] string _name; //如下特性没法应用,由于MsgAttribute定义的特性只能用于类和字段 //[ClassMsg("这是读写姓名字段的属性")] public string Name { get { return _name; } set { _name = value; } } } public class Test { //3. 主函数状况: static void Main(string[] args) { //获取Person类的Type对象tp Type tp = typeof(Person); //获取tp对象的特性数组,并将数组赋值给MyAtts object[] MyAtts = tp.GetCustomAttributes(true); //object[] MyAtts = typeof(Person).GetCustomAttributes(false); //遍历并输出MyAtts数组子项的Msg属性 foreach (ClassMsgAttribute m in MyAtts) { Console.WriteLine("Class Person attribute:{0}", m.Msg); } Console.ReadLine(); } //运行结果: //class person attribute:this is the class for name attribute } }
GetCustomAttributes用于获取程序集的特性,也就是这个程序集合中包含了多少个特性
继续来一个简单的例子来讲明定制特性:
using System;
public class HelpAttribute: Attribute //定制特性至关于一个类
{
//...
}
无论你是否相信,咱们上面已经创建了一个定制特性,如今咱们能够用它来装饰现有的类就好像咱们使用的Obsolete attribute同样。
[Help()]
public class AnyClass
{
//...
}
注意:对于一个特性类使用Attribute后缀是一个惯例。然而,若是不添加编译器会自动添加匹配。
定义或控制特性的使用
AttributeUsage类是另一个预约义特性类,它帮助咱们控制咱们本身的定制特性的使用。它描述一个定制特性如何被使用。
下面经过实例来探讨下AttributeUsage的三个属性
1)咱们将会在刚才的Help特性前放置AttributeUsage特性以期待在它的帮助下控制Help特性的使用。
using System; [AttributeUsage(AttributeTargets.Class), AllowMultiple = false, Inherited = false ] public class HelpAttribute : Attribute { public HelpAttribute(String Description_in) { this.description = Description_in; } protected String description; public String Description { get { return this.description; } } }
先让咱们来看一下AttributeTargets.Class。它规定了Help特性只能被放在class的前面。这也就意味着下面的代码将会产生错误:
[Help("this is a do-nothing class")] public class AnyClass { [Help("this is a do-nothing method")] //error public void AnyMethod() { } } //编译器报告错误以下: AnyClass.cs: Attribute 'Help' is not valid on this declaration type. It is valid on 'class' declarations only.
咱们可使用AttributeTargets.All来容许Help特性被放置在任何程序实体前。可能的值是:
Assembly,
Module,
Class,
Struct,
Enum,
Constructor,
Method,
Property,
Field,
Event,
Interface,
Parameter,
Delegate,
All = Assembly Module Class Struct Enum Constructor Method Property Field Event Interface Parameter Delegate,
ClassMembers = Class Struct Enum Constructor Method Property Field Event Delegate Interface )
下面考虑一下AllowMultiple = false。它规定了特性不能被重复放置屡次。
[Help("this is a do-nothing class")] [Help("it contains a do-nothing method")] public class AnyClass { [Help("this is a do-nothing method")] //error public void AnyMethod() { } } 它产生了一个编译期错误。 AnyClass.cs: Duplicate 'Help' attribute
Ok,如今咱们来讨论一下最后的这个属性。Inherited, 代表当特性被放置在一个基类上时,它可否被派生类所继承。
[Help("BaseClass")] public class Base { } public class Derive : Base { } 这里会有四种可能的组合: [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false ] [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false ] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true ] [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true ]
第一种状况:
若是咱们查询(Query)(稍后咱们会看到如何在运行期查询一个类的特性)Derive类,咱们将会发现Help特性并不存在,由于inherited属性被设置为false。
第二种状况:
和第一种状况相同,由于inherited也被设置为false。
第三种状况:
为了解释第三种和第四种状况,咱们先来给派生类添加点代码:
[Help("BaseClass")]
public class Base
{
}
[Help("DeriveClass")]
public class Derive : Base
{
}
如今咱们来查询一下Help特性,咱们只能获得派生类的属性,由于inherited被设置为true,可是AllowMultiple却被设置为false。所以基类的Help特性被派生类Help特性覆盖了。
第四种状况:
在这里,咱们将会发现派生类既有基类的Help特性,也有本身的Help特性,由于AllowMultiple被设置为true。
自定义了一个特性类:
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method)] class WahAttribute:System.Attribute { private string description; public string Description { get { return description; } set { description = value; } } private string author; public string Author { get { return author; } set { author = value; } } public WahAttribute(string desc) { this.description = desc; } }
运用特性类:
namespace attributeDemo { public class Teacher { private string name; public string Name { get { return name; } set { name = value; } } private int age; public int Age { get { return age; } set { age = value; } } private string sex; public string Sex { get { return sex; } set { sex = value; } } //只有用户名为wah的才能够调用此方法 [Wah("this is my attribute test", Author = "wah", Description = "test")] public string getMessage() { return "好好学习,每天向上"; } [Wah("this is with parameters test",Author="wanggaihui",Description="test with parameters")] public int Test(int a,int b) { return a+b; } } }
处理特性类:
private void button_Click(object sender, EventArgs e) { //获得类型 Type type = typeof(Teacher); //获得此类型全部方法 MethodInfo[] methods = type.GetMethods(); foreach (MethodInfo method in methods) { //获得此方法的全部特性 object[] attributes = method.GetCustomAttributes(false); foreach (object o in attributes) { //判断是不是本身定义的特性 if (o.GetType() == typeof(WahAttribute)) { //强转取得值 WahAttribute waha = (WahAttribute)o; this.listBox1.Items.Add("author=" + waha.Author); this.listBox1.Items.Add("description=" + waha.Description); } } } }
C#特性能够应用于各类类型和成员。加在类前面的是类特性,加在方法前面的是方法特性。不管他们被用在哪里,不管他们之间有什么区别,特性的最主要的目的就是自描述。而且由于特性是能够由本身定制的,而不只仅局限于.net提供的那几个现成的,所以给C#程序开发带来了很大的灵活性。
咱们仍是借用生活中的例子来介绍C#的特性机制吧。
假设有一天你去坐飞机,你就必须提早去机场登机处换登机牌。登机牌就是一张纸,上面写着哪趟航班、由哪里飞往哪里以及你的名字、座位号等等信息,其实,这就是特性。它不须要你生理上包含这些属性(人类出现那会儿还没飞机呢),就像上面的HumanBase类没有IsSerializable这样的属性,特性只须要在类或方法须要的时候加上去就好了,就像你不老是在天上飞同样。
拿到了登机牌,就意味着你能够合法地登机起飞了。但此时你还不知道你要坐的飞机停在哪里,不用担忧,地勤人员会开车送你过去,可是他怎么知道你是哪趟航班的呢?显然仍是经过你手中的登机牌。因此,特性最大的特色就是自描述。
既然是起到描述的做用,那目的就是在于限定。就比如地勤不会把你随便拉到一架飞机跟前就扔上去了事,由于标签上的说明信息就是起到限定的做用,限定了目的地、乘客和航班,任何差错都被视为异常。若是前面的HumanBase不加上Serializable特性就不能在网络上传输。
指定特性参数
若是找到这样的构造函数,编译器就会把指定的元数据传送给程序集。若是找不到,就生成一个这样的构造函数。若是找到一个这样的构造函数,编译器就会把指定的元数据传送给程序集。若是找不到就生成一个编译错误。如后面所述,反射会从程序集中读取元数据,并实例化他们表示的特性类。所以,编译器须要确保存在这样的构造函数,才能在运行期间实例化指定的特性。
参考:https://msdn.microsoft.com/zh-cn/library/67ef8sbd(v=vs.80).aspx http://zhidao.baidu.com/link?url=FArDop5J7k64OP9APCOZRfRNJ-Y6QcAvI0Y5Dt-4bXh-lxhpImjC1ugmxAv6il9Pc-blnU9PPnsQLpZNr-575qhttp://blog.csdn.net/helloguonan/article/details/5912032 https://msdn.microsoft.com/zh-cn/library/z0w1kczw(v=vs.110).aspxhttp://www.cnblogs.com/rohelm/archive/2012/04/19/2456088.html