特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签,可以为程序集、类型,以及类型内部的各种成员添加扩展信息,用于表示一些附加信息。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。通常,表示特性的类都派生自System.Attribute类。下面来看几个特殊的特性:

AttributeUsage

预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。

规定该特性的语法如下:

[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]

其中:

  • 参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All
  • 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
  • 参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。

例如:

[AttributeUsage(AttributeTargets.Class|
AttributeTargets.Struct|
AttributeTargets.Enum|
AttributeTargets.Delegate|
AttributeTargets.Method|
AttributeTargets.Property|
AttributeTargets.Field|
AttributeTargets.Constructor|
AttributeTargets.Event,
AllowMultiple=true,Inherited=false)]

再看下面代码:

        [Serializable]
public class A { }

从上面代码,可以看出,SerializableAttribute特性将应用于A类,在C#中,可以略去“Attribute”,直接写“Serializable”即可。SerializableAttribute类的原型定义如下:

        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct |
AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)]
[ComVisible(true)]
public sealed class SerializableAttribute : Attribute { }

从查看SerializableAttribute类的定义我们又发现,特性类除了从Attribute派生外,也可以向其附加特性,而用的最多的就是AttributeUsageAttribute特性,它的定义如下:

        [Serializable]
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
[ComVisible(true)]
public sealed class AttributeUsageAttribute : Attribute { }

在定义AttributeUsageAttribute类时也附加了自身作为类的特性,该类指定特性类的适用范围,用AttributeTargets枚举来表示。

如果特性存在带参数的构造函数,可以在特性后用小括号包裹起来,然后传递参数。当然也可以为特性类的属性或字段赋值,也可以同时加多个特性。

自定义特性

定义特性类与定义普通类一样的,既可以声明构造函数、字段、属性、方法等成员,也可以派生子类,但必须从System.Attribute类或System.Attribute的子类派生。

例如:

[AttributeUsage(AttributeTargets.Class
| AttributeTargets.Method
| AttributeTargets.Property)]
public class MyAttribute : Attribute
{
public string Title { get; set; }
public string VerNo { get; set; }
}
//特性用于类
[My(Title="draw" ,VerNo="1.0.2")]
public class Drawer
{
//特性用于属性
[My(Title = "color", VerNo = "1..0.2")]
public string Color { get; set; }
//特性用于方法
[My(Title = "color", VerNo = "1.0.2")]
public void Draw() { }
//特性用于字段,编译错误
[My(Title = "thick", VerNo = "1.0.2")]
public int a;
}

注意,当MyAttribute特性用于字段时,会发生编译错误,因为MyAttribute类在定义是已经指明它只能用于类、方法、属性,但并未指明其可用于字段。

在默认条件下,特性将应用于更随其后的对象,同理,也可以为方法中的参数应用特性。如下代码所示:

        public static string Run([In]string pt,[Optional]int x)
{
return string.Empty;
}

为参数应用特性秩序放在参数前面即可。但是,如果要为返回值应用特性,那么是不是把特性放在返回值前面就可以了呢?就像这样:

public [MarshalAs(UnmanagedType.SysInt] int Compute(){}

这样做是错误的,编译无法通过,那又如何实现呢?

在前面也提到,在默认的情况下,特性是应用于跟随其后的对象的,因此,在许多时候,使用特性是都会省略了表示特性目标的关键字。以下是特性应用于目标对象时的完整格式:

[<目标>:<特性列表>]

下面列举特性目标关键字及相关说明:

assembly:表示特性将应用于当前程序集,通常放在程序集中命名空间或所有类型定义之前。

module:用于当前模块。

field:用于字段,如果特性后紧跟着字段的声明代码,则该关键字可以省略。

event:用于事件。

method:用于方法,也可以用于属性中的get和set访问器。

param:用于方法中的参数或属性定义中的set访问器中的参数(value)。

property:用于属性。

type:用于类型。

return:用于方法的返回值,属性中的get访问器的返回值。

通过上面的介绍,相信你已经知道如何为方法的返回值应用特性了。即将特性应用到方法上,并且注明特性的应用目标为return。如下面代码所示:

        [return: MarshalAs(UnmanagedType.SysInt)]
public int Compute() { return ; }

通过反射技术检索特性

下面将介绍如何查找特性,需要用到反射技术。前面讲过,特性可以理解为附加在类型上的扩展信息,可以通过在类型中找到指定的特性来验证代码的调用方法是否符合特定的要求。

下面看个例子即可:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace My
{
[AttributeUsage(AttributeTargets.All)]
public class TypeInfoAttribute : Attribute
{
public string Description { get; set; }
} [TypeInfo(Description = "这是我们定义的枚举类型。")]
enum TestEnum { One = , Two, Three } [TypeInfo(Description = "这是我们定义的一个类。")]
public class Goods { } class Program
{
static void Main(string[] args)
{
// 用Type类的GetCustomAttributes方法可以获取指定类型上附加的特性列表
// 返回一个object类型的数组,数组中的每个元素表示一个特性类的实例
// GetCustomAttributes方法的其中一个重载可以将一个Type作为参数传递
// 该Type表示要获取的特性的类型,typeof运算符返回某个类型的一个Type
// 本例中我们要获取TypeInfoAttribute特性列表
// 由于上面定义TestEnum枚举和Goods类时,只应用了一个TypeInfoAttribute特性
// 因此获取到的特性实例数组的元素个数总为1 object[] attrs = typeof(TestEnum).GetCustomAttributes(typeof(TypeInfoAttribute), false);
if (attrs.Length > )
{
TypeInfoAttribute ti = (TypeInfoAttribute)attrs[];
Console.WriteLine("TestEnum枚举的描述信息:{0}", ti.Description);
} attrs = typeof(Goods).GetCustomAttributes(typeof(TypeInfoAttribute), false);
if (attrs.Length > )
{
TypeInfoAttribute ti = (TypeInfoAttribute)attrs[];
Console.WriteLine("Goods类的描述信息:{0}", ti.Description);
} Console.Read();
}
}
}

结果:

TestEnum枚举的描述信息:这是我们定义的枚举类型。
Goods类的描述信息:这是我们定义的一个类。

通过以上的介绍,相信你已经对特性有了一定的了解。博客写到这里,我也不啰嗦了。

特性(C#)的更多相关文章

  1. Fis3的前端工程化之路[三大特性篇之声明依赖]

    Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...

  2. Fis3的前端工程化之路[三大特性篇之资源定位]

    Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...

  3. Fis3的前端工程化之路[三大特性篇之内容嵌入]

    Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...

  4. .NET 4.6.2正式发布带来众多特性

    虽然大多数人的注意力都集中在.NET Core上,但与原来的.NET Framework相关的工作还在继续..NET Framework 4.6.2正式版已于近日发布,其重点是安全和WinForms/ ...

  5. SQL Server 2014 新特性——内存数据库

    SQL Server 2014 新特性——内存数据库 目录 SQL Server 2014 新特性——内存数据库 简介: 设计目的和原因: 专业名词 In-Memory OLTP不同之处 内存优化表 ...

  6. 探索ASP.NET MVC5系列之~~~4.模型篇---包含模型常用特性和过度提交防御

    其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...

  7. InnoDB关键特性学习笔记

    插入缓存 Insert Buffer Insert Buffer是InnoDB存储引擎关键特性中最令人激动与兴奋的一个功能.不过这个名字可能会让人认为插入缓冲是缓冲池中的一个组成部分.其实不然,Inn ...

  8. ElasticSearch 5学习(10)——结构化查询(包括新特性)

    之前我们所有的查询都属于命令行查询,但是不利于复杂的查询,而且一般在项目开发中不使用命令行查询方式,只有在调试测试时使用简单命令行查询,但是,如果想要善用搜索,我们必须使用请求体查询(request ...

  9. HTML5新特性有哪些,你都知道吗

    一.画布(Canvas) 画布是网页中的一块区域,可所以用JavaScript在上面绘图.下面我们来创建一个画布并在上面绘制一个坦克(后面将用HTML5做一个坦克大战游戏),代码如下: <!DO ...

  10. C++11特性——变量部分(using类型别名、constexpr常量表达式、auto类型推断、nullptr空指针等)

    #include <iostream> using namespace std; int main() { using cullptr = const unsigned long long ...

随机推荐

  1. 如何在Windows7上完全卸载Oracle 11g(转)

    http://blog.csdn.net/haishu_zheng/article/details/19180081

  2. 你写的Try...Catch真的有必要么?

    很多人喜欢用Try...Catch把每一个方法都包裹起来,可是真的有必要么? 为什么要这样做?我估计是大家被BUG吓怕了,生怕生产环境出现各种莫名其妙的错误,比如最经典的NullReferenceEx ...

  3. 作业三: 代码规范、代码复审、PSP

    分) 对于是否需要有代码规范,请考虑下列论点并反驳/支持: 这些规范都是官僚制度下产生的浪费大家的编程时间.影响人们开发效率, 浪费时间的东西. 我是个艺术家,手艺人,我有自己的规范和原则. 规范不能 ...

  4. ECMAScript 6入门

    预计在2014年底,ECMAScript 6将会正式发布,他的草案在13年3月份被冻结,后续提出新特性将会移至ECMASript 7中.目前还没有哪款浏览器实现了ES6的全部内容,兼容性最强的一款要数 ...

  5. CentOS 7 网络配置

    Virtual box 安装了CentOS 7最小模式后马上用ifconfig命令查看网络情况,发现该命令不存在. [root@centos1 ~]# ifconfig -bash: ifconfig ...

  6. 《Node web开发》笔记

    还是因为学习kibana,才开始了解node. Node是一种基于事件驱动的异步系统,基于Chrome的引擎V8. Node中由于大量的使用模块,因此出现了很多开源模块,有点像java社区的样子. 笔 ...

  7. Sublime Text3 配置 NodeJs 环境

    前言 大家都知道,Sublime Text 安装插件一般从 Package Control 中直接安装即可,当我安装 node js 插件时候,直接通过Package Control 安装,虽然插件安 ...

  8. Atitit sql计划任务与查询优化器--统计信息模块

    Atitit sql计划任务与查询优化器--统计信息模块 每一个统计信息的内容都包含以上三部分的内容. 我们依次来分析下,通过这三部分内容SQL Server如何了解该列数据的内容分布的. a.统计信 ...

  9. c#设计模式-适配器模式

    一. 适配器(Adapter)模式 适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作. 名称由来 这很像变压器(Adapter),变压 ...

  10. Python 学习之路 (前言)

    为什么要学Python  1,脚本语言本身很方便简洁,未来会有趋势 2,web 方向 3,运维方向 我是学静态语言出身的,java,毕业后从事android 应用开发,曾在工作期间学习过linux,想 ...