特性(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. DPC和ISR的理解

    首先来说中断 计算机的中断分为软中断和硬中断,即IRQL和DIRQL,共32个级别,从0~31级别依次提升,0~2属于软中断 一般线程运行于PASSIVE_LEVEL级别,如果不想在运行时切换到其他线 ...

  2. Struts2

    为什么要用Struts2? 这里列举一些Servlet的缺点: 1.每写一个servlet在web.xml中都要做相应的配置.如果有多很servlet,会导致web.xml内容过于繁多. 2.这样的结 ...

  3. poj3750-小孩报数问题(约瑟夫环)

    一,题意: 中文题.二,思路: 1,输入. 2,无限循环1~n~1~n,直到输出n次,再跳出. 3,输出名字,并标记. 普通模拟版: #include<iostream> #include ...

  4. ABP理论学习之内嵌资源文件

    返回总目录 本篇目录 介绍 创建内嵌文件 暴露内嵌文件 使用内嵌文件 介绍 在一个web应用中,有供客户端使用的javascript,css,xml等文件.它们一般是作为分离的文件被添加到web项目中 ...

  5. 大白话讲解Promise(三)搞懂jquery中的Promise

    前两篇我们讲了ES6中的Promise以及Promise/A+规范,在Promise的知识体系中,jquery当然是必不可少的一环,所以本篇就来讲讲jquery中的Promise,也就是我们所知道的D ...

  6. C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字

    C#+OpenGL+FreeType显示3D文字(3) - 用PointSprite绘制文字 上一篇实现了把文字绘制到OpenGL窗口,但实质上只是把含有文字的贴图贴到矩形模型上.本篇我们介绍用Poi ...

  7. MVC缓存

    MVC入门系列教程-视频版本,已入驻51CTO学院,文本+视频学效果更好哦.视频链接地址如下: 点我查看视频.另外,针对该系列教程博主提供有偿技术支持,群号:226090960,群内会针对该教程的问题 ...

  8. angularjs controller 继承

    前沿 最近在angularjs项目当中,看到 controller 好多都是重复性的代码,在 controller 当中有好多代码很相似 function(比如 controller 下的 CRUD ...

  9. angular-ui分页组件

    http://angular-ui.github.io/bootstrap/#/pagination 分页组件只提供生成分页按钮,数据的显示需要使用ng-repeat, 注意设置 items-per- ...

  10. ExtJs4常用配置方法备忘

    viewport布局常用属性 new Ext.Viewport({ layout: "border", renderTo: Ext.getBody(), defaults: { b ...