特性(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. IDT HOOK思路整理

    IDT(中断描述符表)分为IRQ(真正的硬件中断)和软件中断(又叫异常). HOOK的思路为,替换键盘中断处理的函数地址为自己的函数地址.这样在键盘驱动和过滤驱动之前就可以截获键盘输入. 思路确定之后 ...

  2. js表达式与语句的区别

    http://www.2ality.com/2012/09/expressions-vs-statements.html http://www.jb51.net/article/31298.htm 表 ...

  3. 一步步学习javascript基础篇(2):作用域和作用域链

    作用域和作用域链 js的语法用法非常的灵活,且稍不注意就踩坑.这集来分析下作用域和作用域链.我们且从几道题目入手,您可以试着在心里猜想着答案. 问题一. if (true) { var str = & ...

  4. Webstorm 10 for mac osx 注册机,序列号,kegen

    小菜最近get到mac体验机会,早就耳闻mac非常适合做开发,于是迫不及待的安装各种开发工具,不知不觉,轮到前端开发神器webstorm了,看了一下官网的价格,心拔凉拔凉的. 果断搜索注册机,搜到的结 ...

  5. 为jQuery添加Webkit的触摸方法支持

    前些日子收到邮件,之前兼职的一个项目被转给了其他人,跟进的人来问我相关代码的版权问题. 我就呵呵了. 这段代码是我在做13年一份兼职的时候无聊加上去的,为jQuery添加触摸事件的支持.因为做得有点无 ...

  6. 解决记录日志导致VS2013缓慢的问题

    最近VS2013启动时.编译时.显示项目属性速度狂慢,遇到项目多的,显示项目属性时甚至VS挂掉. 把所有的VS插件卸载.甚至重装VS也不见效果. 用ProcessMonitor发现VS一直在Defau ...

  7. solve the problem of 'java web project cannot display verification code'

    my java code of the function: package com.util; import java.awt.Color; import java.awt.Font; import ...

  8. 每天一个linux命令(25):linux文件属性详解

    Linux 文件或目录的属性主要包括:文件或目录的节点.种类.权限模式.链接数量.所归属的用户和用户组.最近访问或修改的时间等内容.具体情况如下: 命令: ls -lih 输出: [root@loca ...

  9. WinRAR 4.20 beta2 key!注册文件 注册码

    WinRAR 4.20 beta2 key!注册文件 注册码 WinRAR 4.20 beta2注册文件 WinRAR 4.20 beta2 working key ================= ...

  10. SVN+码云 简单使用流程

    1.登录码云网站...