C#特性学习笔记一

时间:2023-11-14 12:29:20

元数据,就是C#中封装的一些类,无法修改.类成员的特性被称为元数据中的注释.

1、什么是特性

  1)属性与特性的区别

属性(Property):属性是面向对象思想里所说的封装在类里面的数据字段,Get,Set方法。

特性(Attribute):  官方解释:特性是给指定的某一声明的一则附加的声明性信息。 允许类似关键字的描述声明。它对程序中的元素进行标注,如类型、字段、方法、属性等。从.net角度看,特性是一种 类,这些类继承于System.Attribute类,用于对类、属性、方法、事件等进行描述,主要用在反射中。但从面向对象的级别看,其实Attribute是类型级别的,而不是对象级别。

Attributes和.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

自定义特定:

  1. //限定特性类的应用范围
  2. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
  3. //定制MsgAttribute特性类,继承于Attribute
  4. public class ClassMsgAttribute : Attribute
  5. {
  6. //定义_msg字段和Msg属性//Msg属性用于读写msg字段
  7. string _msg;
  8. public string Msg { get { return _msg; } set { _msg = value; } }
  9. public ClassMsgAttribute() { }
  10. //重载构造函数接收一个参数,赋值给_msg字段
  11. public ClassMsgAttribute(string s) { _msg = s; } 
  12. }

//限定特性类的应用范围
[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; } 
}

调用:

  1. //在Person类上标记ClassMsg特性
  2. [ClassMsg(Msg = "这是关于人的姓名信息的类")]
  3. class Person
  4. {
  5. //在_name字段上应用ClassMsg特性
  6. [ClassMsg("这是存储姓名的字段")]
  7. string _name;
  8. //以下特性无法应用,因为MsgAttribute定义的特性只能用于类和字段
  9. //[ClassMsg("这是读写姓名字段的属性")]
  10. public string Name { get { return _name; } set { _name = value; } }
  11. }

//在Person类上标记ClassMsg特性
[ClassMsg(Msg = "这是关于人的姓名信息的类")]
class Person
{
//在_name字段上应用ClassMsg特性
[ClassMsg("这是存储姓名的字段")]
string _name;
//以下特性无法应用,因为MsgAttribute定义的特性只能用于类和字段
//[ClassMsg("这是读写姓名字段的属性")]
public string Name { get { return _name; } set { _name = value; } }
}

主函数的情况

  1. static void Main(string[] args)
  2. {
  3. //获取Person类的Type对象tp
  4. Type tp = Type.GetType("Person");
  5. //获取tp对象的特性数组,并将数组赋值给MyAtts
  6. object[] MyAtts = tp.GetCustomAttributes(false);
  7. //遍历并输出MyAtts数组子项的Msg属性
  8. foreach (ClassMsgAttribute m in MyAtts)
  9. {
  10. Console.WriteLine("Person类的特性:{0}", m.Msg);
  11. }

static void Main(string[] args)
{
//获取Person类的Type对象tp
Type tp = Type.GetType("Person");
//获取tp对象的特性数组,并将数组赋值给MyAtts
object[] MyAtts = tp.GetCustomAttributes(false);
//遍历并输出MyAtts数组子项的Msg属性
foreach (ClassMsgAttribute m in MyAtts)
{
Console.WriteLine("Person类的特性:{0}", m.Msg);
}

GetCustomAttributes用于获取程序集的特性,也就是这个程序集合中包含了多少个特性

继续来一个简单的例子来说明定制特性:

using System;

public class HelpAttribute: Attribute  //定制特性相当于一个类

{

//...

}

不管你是否相信,我们上面已经建立了一个定制特性,现在我们可以用它来装饰现有的类就好像我们使用的Obsolete attribute一样。

[Help()]

public class AnyClass

{

//...

}

注意:对于一个特性类使用Attribute后缀是一个惯例。然而,如果不添加编译器会自动添加匹配。

定义或控制特性的使用

AttributeUsage类是另外一个预定义特性类,它帮助我们控制我们自己的定制特性的使用。它描述一个定制特性如何被使用。

下面通过实例来探讨下AttributeUsage的三个属性

1)我们将会在刚才的Help特性前放置AttributeUsage特性以期待在它的帮助下控制Help特性的使用。  

  1. using System;
  2.   [AttributeUsage(AttributeTargets.Class), AllowMultiple = false,
  3.   Inherited = false ]
  4.   public class HelpAttribute : Attribute
  5.   {
  6.   public HelpAttribute(String Description_in)
  7.   {
  8.   this.description = Description_in;
  9.   }
  10.   protected String description;
  11.   public String Description
  12.   {
  13.   get
  14.   {
  15.   return this.description;
  16.   }
  17.   }
  18.   }

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的前面。这也就意味着下面的代码将会产生错误:

  1. [Help("this is a do-nothing class")]
  2. public class AnyClass
  3. {
  4. [Help("this is a do-nothing method")] //error
  5. public void AnyMethod()
  6. {
  7. }
  8. }
  9. //编译器报告错误如下:  
  10. AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
  11. It is valid on 'class' declarations only.   

   [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。它规定了特性不能被重复放置多次。  

  1. [Help("this is a do-nothing class")]
  2.   [Help("it contains a do-nothing method")]
  3.   public class AnyClass
  4.   {
  5.   [Help("this is a do-nothing method")] //error
  6.   public void AnyMethod()
  7.   {
  8.   }
  9.   }
  10.   它产生了一个编译期错误。 
  11.   AnyClass.cs: Duplicate 'Help' attribute 

[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, 表明当特性被放置在一个基类上时,它能否被派生类所继承。  

  1. [Help("BaseClass")]
  2. public class Base
  3. {
  4. }
  5. public class Derive : Base
  6. {
  7. }
  8. 这里会有四种可能的组合: 
  9. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false ]
  10. [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false ]
  11. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true ]
  12. [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true ]

   [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。