(转)C#特性详解
本文转载自:http://www.cnblogs.com/rohelm/archive/2012/04/19/2456088.html
特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集、类型、方法、属性等)相关联。特性与程序实体关联后,即可在运行时使用名为“反射”的技术查询特性。这篇文章绝大部分是按照MSDN来学习的,只是加了一点点自己的东东,官方介绍的很详细,我们就一起来了解一下它的用法。
特性具有以下属性:
特性可向程序中添加元数据。元数据是有关在程序中定义的类型的信息。所有的 .NET 程序集都包含指定的一组元数据,这些元数据描述在程序集中定义的类型和类型成员。可以添加自定义特性,以指定所需的任何附加信息。
可以将一个或多个特性应用到整个程序集、模块或较小的程序元素(如类和属性)。
特性可以与方法和属性相同的方式接受参数。
- 程序可以使用反射检查自己的元数据或其他程序内的元数据。
这些都是官方的定义,那么对于一个初学者来说,看的懂汉字不难,但是上面的元数据是什么?
我这么通俗的解释下:
你注意过程序及编译的时候的pdb文件了吗?pdb文件里面存储了,关于程序集内部的所有的成员信息,例如,成员的数据类型,属性类型,方法返回值,方法入参类型,就是程序及内部所有的定义信息的数据信息,是存储定义信息的一类数据信息,程序集里面的所有的关于声明类的数据信息,包括方法间调用,都是存储在元数据里面。
下面开始一同学习特性的用法:
特性可以放置在几乎所有的声明中。在 C# 中,特性的指定方法为:将括在方括号中的特性名置于其应用到的实体的声明上方。
[System.Serializable]
public class SampleClass
{
// Objects of this type can be serialized.
}
一个声明上可放置多个特性:

using System.Runtime.InteropServices;
...
void MethodA([In][Out] ref double x) { }
void MethodB([Out][In] ref double x) { }
void MethodC([In, Out] ref double x) { }

某些特性对于给定实体可以指定多次。例如,ConditionalAttribute 就是一个可多次使用的特性:
[Conditional("DEBUG"), Conditional("TEST1")]
void TraceMethod()
{
// ...
}
根据约定,所有特性名称都以单词“Attribute”结束,以便将它们与“.NET Framework”中的其他项区分。但是,在代码中使用特性时,不需要指定 attribute 后缀。例如,[DllImport] 虽等效于 [DllImportAttribute],但 DllImportAttribute 才是该特性在 .NET Framework 中的实际名称
特性参数
许多特性都有参数,而这些参数可以是定位参数、未命名参数或命名参数。任何定位参数都必须按特定顺序指定并且不能省略,而命名参数是可选的且可以按任意顺序指定。首先指定定位参数。例如,这三个特性是等效的:
[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]
第一个参数(DLL 名称)是定位参数并且总是第一个出现,其他参数为命名参数。在这种情况下,两个命名参数均默认为 false,因此可将其省略。有关默认参数值的信息,可以参考参考各个特性的文档。
特性目标
特性的目标是应用该特性的实体。例如,特性可以应用于类、特定方法或整个程序集。默认情况下,特性应用于它后面的元素。但是,您也可以显式标识要将特性应用于方法还是它的参数或返回值。
若要显式标识特性目标,请使用下面的语法:
[target : attribute-list]
下表显示了可能的 target 值的列表。
|
C# |
适用对象 |
|---|---|
|
assembly |
整个程序集 |
|
module |
当前程序集模块(不同于 Visual Basic 模块) |
|
field |
在类或结构中的字段 |
|
event |
Event |
|
method |
方法或 get 和 set 属性访问器 |
|
param |
方法参数或 set 属性访问器参数 |
|
property |
Property |
|
return |
方法、属性索引器或 get 属性访问器的返回值 |
|
type |
结构、类、接口、枚举或委托 |
下面的示例演示如何将特性应用于程序集和模块。
using System;
using System.Reflection;
[assembly: AssemblyTitleAttribute("Production assembly 4")]
[module: CLSCompliant(true)]
下面的示例演示如何在 C# 中将特性应用于方法、方法参数和方法返回值。

// default: applies to method
[SomeAttr]
int Method1() { return 0; } // applies to method
[method: SomeAttr]
int Method2() { return 0; } // applies to return value
[return: SomeAttr]
int Method3() { return 0; }

无论规定 SomeAttr 应用于什么目标,都必须指定 return 目标,即使 SomeAttr 被定义为仅应用于返回值也是如此。换言之,编译器将不使用 AttributeUsage 信息解析不明确的特性目标。
呀,终于了解完了,是时候自己十一下手了,我们就动手活动一下颈骨,多加点注释来关联一下学过的内容。
我们这里拿ObsoleteAttribute做下测试,它标记不再使用的程序元素。此类不能被继承。首先我们看一下它的继承结构。
当然我们看看其他的特性,我们就会发现特性其实是从System.Object类派生出来的一种特殊类。
我们现在用这个构造来验证
public ObsoleteAttribute(string message, bool error)
参数 类型:
message System ..::.String 描述可选的变通方法的文本字符串。
error System ..::.Boolean 指示是否将使用已过时的元素视为错误的布尔值。
总之我们在使用特性的时候不要产生畏惧,就当他是特殊的类,以前怎么样用构造函数现在仍旧怎么用只是格式有点微妙的变化。

using System; namespace 特性
{
class Program
{
static void Main(string[] args)
{
OldClass old = new OldClass();//2个报错,因为使用OldClass两次
old.OldMethod();//警告。因为第二个参数未指定使用已过时的元素不会视为错误
Console.ReadKey();
}
} [Obsolete("该类已经过时",true)]//使用默认的特性目标,直接作用于紧随其后的Class OldClass
//第二个参数我这里设置为true将使用已过时的元素视为错误
class OldClass
{
[method: Obsolete("该方法已经过时")]
public void OldMethod()
{
Console.WriteLine("过时的方法!");
}
}
}

运行以后会出现两个错误提示,一个警告提示:

好了,现在我们在紧接着学习自定义特性,这个估计就算是相当简单了。
自定义特性
通过定义一个特性类,可以创建您自己的自定义特性。该特性类直接或间接地从Attribute派生,有助于方便快捷地在元数据中标识特性定义。假设您要用编写类型的程序员的名字标记类型。可以定义一个自定义 Author特性类:

[System.AttributeUsage(System.AttributeTargets.Class |
System.AttributeTargets.Struct)
]
public class Author : System.Attribute
{
private string name;
public double version; public Author(string name)
{
this.name = name;
version = 1.0;
}
}

类名是特性的名称,即 Author。它由 System.Attribute 派生而来,因此是自定义特性类。构造函数的参数是自定义特性的定位参数。本示例中 name 是定位参数。任何公共的读写字段或属性都是命名参数。在本例中,version 是唯一的命名参数。请注意 AttributeUsage 特性的用法,它使得 Author 特性仅在类声明中有效。
可以按如下所示使用此新特性:
[Author("P. Ackerman", version = 1.1)]
class SampleClass
{
// P. Ackerman's code goes here...
}
AttributeUsage 有一个命名参数 AllowMultiple,使用它可以使自定义特性成为一次性使用或可以使用多次的特性。在下面的代码示例中,创建了一个使用多次的特性。
[System.AttributeUsage(System.AttributeTargets.Class |
System.AttributeTargets.Struct,
AllowMultiple = true) // multiuse attribute
]
public class Author : System.Attribute
在下面的代码示例中,向某个类应用了同一类型的多个特性。

[Author("P. Ackerman", version = 1.1)]
[Author("R. Koch", version = 1.2)]
class SampleClass
{
// P. Ackerman's code goes here...
// R. Koch's code goes here...
}

如果特性类包含一个属性,则该属性必须为读写属性。
介绍完了官方的示例是不是还是云里雾里,那么我们一起来深入解剖一下。
首先我们从上面可以总结出创建自定义特性的大概步骤:
1.应用AttributeUsage特性 虽然等效,但AttributeUsageAttribute 才是该特性在 .NET Framework 中的实际名称,它也是由System.Attribute 派生而来。
2.声明特性类,它由 System.Attribute 派生而来。
3.声明构造函数
4.声明特性
OVER!!!就这么回事,完了吗,我们继续剖析之重要的信息,AttributeUsage特性。
AttributeUsage特性,研究特性当然首要的要研究其构造函数。现在我们来看看他是怎么定义的。
public AttributeUsageAttribute( AttributeTargets validOn)
参数 validOn 类型:System.AttributeTargets 使用按位“或”运算符组合的一组值,用于指示哪些程序元素是有效的。
用指定的 AttributeTargets、AllowMultiple 值和 Inherited 值列表初始化 AttributeUsageAttribute 类的新实例。
于是乎我们返回到了研究AttributeTargets的问题了。现在我们就来细心的看看他是神马!
原来他是一个枚举,通过该特性可使其成员值按位组合。可以通过按位“或”运算组合 AttributeTargets 枚举值来获得首选组合。
成员
| 成员名称 | 说明 | |
|---|---|---|
![]() ![]() |
Assembly | 可以对程序集应用特性。 |
![]() ![]() |
Module | 可以对模块应用特性。
注意Module 指的是可移植的可执行文件(.dll 或 .exe),而非 Visual Basic 标准模块。
|
![]() ![]() |
Class | 可以对类应用特性。 |
![]() ![]() |
Struct | 可以对结构应用特性,即值类型。 |
![]() ![]() |
Enum | 可以对枚举应用特性。 |
![]() ![]() |
Constructor | 可以对构造函数应用特性。 |
![]() ![]() |
Method | 可以对方法应用特性。 |
![]() ![]() |
Property | 可以对属性应用特性。 |
![]() ![]() |
Field | 可以对字段应用特性。 |
![]() ![]() |
Event | 可以对事件应用特性。 |
![]() ![]() |
Interface | 可以对接口应用特性。 |
![]() ![]() |
Parameter | 可以对参数应用特性。 |
![]() ![]() |
Delegate | 可以对委托应用特性。 |
![]() ![]() |
ReturnValue | 可以对返回值应用特性。 |
![]() ![]() |
GenericParameter | 可以对泛型参数应用特性。
注意目前,此特性可以应用仅于 C#、Microsoft 中间语言 (MSIL) 和发出的代码。
|
![]() ![]() |
All | 可以对任何应用程序元素应用特性。 |
到了这里一节也就明了了,谜底都一一展现在我们的面前。
按照上面的经验,再次开始动手来熟悉这一切,我指定了该自定义的特性不可继承,就在不解释别的了只是为了证明一下命名参数Inherited定性成功与否,总之还是很简单的。
<span style="font-family: 黑体;">using System; namespace 特性
{
class Program
{
static void Main(string[] args)
{
GetAttributeInfo(typeof(OldClass));
Console.WriteLine("==============");
GetAttributeInfo(typeof(NewClass));
Console.ReadKey();
}
public static void GetAttributeInfo(Type t)
{
OldAttribute myattribute = (OldAttribute)Attribute.GetCustomAttribute(t, typeof(OldAttribute));
if (myattribute == null)
{
Console.WriteLine(t.ToString()+"类中自定义特性不存在!");
}
else
{
Console.WriteLine("特性描述:{0}\n加入事件{1}", myattribute.Discretion, myattribute.date);
}
}
} [AttributeUsage(AttributeTargets.Class,Inherited=false)]//设置了定位参数和命名参数 //该特性适用于所有的类,而且是非继承的。
class OldAttribute : Attribute//继承自Attribute
{
private string discretion; public string Discretion
{
get { return discretion; }
set { discretion = value; }
}
public DateTime date;
public OldAttribute(string discretion)
{
this.discretion = discretion;
date = DateTime.Now;
}
}
//现在我们定义两类
[Old("这个类将过期")]//使用定义的新特性
class OldClass
{
public void OldTest()
{
Console.WriteLine("测试特性");
}
}
class NewClass:OldClass
{
public void NewTest()
{
Console.WriteLine("测试特性的继承");
}
}
//我们写一个方法用来获取特性信息
}
</span>
运行效果:

今天就到此了,睡觉觉了!希望同学们能略有所获。
(转)C#特性详解的更多相关文章
- C#中的 特性 详解(转载)
本篇幅转载于:http://www.cnblogs.com/rohelm/archive/2012/04/19/2456088.html C#中特性详解 特性提供了功能强大的方法,用于将元数据或声明信 ...
- iOS开发——高级特性&Runtime运行时特性详解
Runtime运行时特性详解 本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的动态特性,使这门古老的语言焕发生机.主要内容如下: 引言 ...
- ES6,ES2105核心功能一览,js新特性详解
ES6,ES2105核心功能一览,js新特性详解 过去几年 JavaScript 发生了很大的变化.ES6(ECMAScript 6.ES2105)是 JavaScript 语言的新标准,2015 年 ...
- 《Android群英传》读书笔记 (5) 第十一章 搭建云端服务器 + 第十二章 Android 5.X新特性详解 + 第十三章 Android实例提高
第十一章 搭建云端服务器 该章主要介绍了移动后端服务的概念以及Bmob的使用,比较简单,所以略过不总结. 第十三章 Android实例提高 该章主要介绍了拼图游戏和2048的小项目实例,主要是代码,所 ...
- C#各个版本中的新增特性详解
序言 自从2000年初期发布以来,c#编程语言不断的得到改进,使我们能够更加清晰的编写代码,也更加容易维护我们的代码,增强的功能已经从1.0搞到啦7.0甚至7.1,每一次改过都伴随着.NET Fram ...
- ASP.NET Core Web服务器 Kestrel和Http.sys 特性详解
ASP.NET Core Web服务器 Kestrel和Http.sys 特性详解 1.1. 名词解释 1.2. Kestrel基本工作原理 1.2.1. Kestrel的基本架构 1.2.2. Ke ...
- Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验
Android群英传笔记--第十二章:Android5.X 新特性详解,Material Design UI的新体验 第十一章为什么不写,因为我很早之前就已经写过了,有需要的可以去看 Android高 ...
- 单元测试系列之十一:Jmockit之mock特性详解
本文是Jmockit学习过程中,根据官网所列的工具特性进行解读. 1.调用次数约束(Invocation count constraints) 可以通过调用计数约束来指定预期和/或允许匹配给定期望的调 ...
- Java9 新特性 详解
作者:木九天 < Java9 新特性 详解 > Java9 新特性 详解 摘要: 1.目录结构 2.repl工具 jShell命令 3.模块化 4.多版本兼容jar包 5.接口方 ...
- [转帖]ASP.NET Core Web服务器 Kestrel和Http.sys 特性详解
ASP.NET Core Web服务器 Kestrel和Http.sys 特性详解 https://www.cnblogs.com/vipyoumay/p/7525478.html ASP.NET C ...
随机推荐
- B-树 C++模板类封装(有图有真相)
定义: 一棵m阶B-树是拥有以下性质的多路查找树: 1.非叶子结点的根结点至少拥有两棵子树: 2.每一个非根且非叶子的结点含有k-1个关键字以及k个子树,其中⌈m/2⌉≤k≤m: 3.每一个叶子结点都 ...
- Redis 高级实用特性
1.安全性 设置客户端连接后进行任何其他操作前先验证密码. 因为Redis速度相当快,所以在一台比较好的服务器下,一个外部用户可以在一秒钟进行150K次的密码尝试,这意味着需要指定一个非常强大的密码来 ...
- linux+udp+server+client
一.客户端 #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include ...
- FIND_IN_SET的简单使用
FIND_IN_SET(str,strlist)函数 str 要查询的字符串 strlist 字段名 参数以”,”分隔 如 (1,2,6,8) 查询字段(strlist)中包含(str)的结果,返回结 ...
- 【arc101】比赛记录
这场还好切出了D,rt应该能涨,然而这场的题有点毒瘤,700分的D没多少人切,更别说EF了.(暴打出题人)既然这样,干脆就水一篇博客,做个简单的比赛记录. C - Candles 这题是一道一眼题,花 ...
- centos7安装MPlyaer
最近更换了centos7系统,对新系统的操作不是太熟悉.大神轻喷.昨晚突然想要下个电影看看,结果发现系统自带的播放器支持的视频格式有限,google查了一下,他们推荐使用MPlayer.于是经过一通g ...
- keystone cache
http://docs.openstack.org/juno/config-reference/content/section_keystone.conf.html http://docs.opens ...
- 184. Department Highest Salary
问题描述 解决方案 select b.Name Department,a.Name Employee,a.Salary from ( select e1.Salary,e1.Name,e1.Depar ...
- php实现微信扫码自动登陆与注册功能
本文实例讲述了php实现微信扫码自动登陆与注册功能.分享给大家供大家参考,具体如下: 微信开发已经是现在程序员必须要掌握的一项基本的技术了,其实做过微信开发的都知道微信接口非常的强大做起来也非常的简单 ...
- 解析Ceph: 数据的端到端正确性和 Scrub 机制
转自:https://www.ustack.com/blog/ceph-internal-scrub/ Ceph 的主要一大特点是强一致性,这里主要指端到端的一致性.众所周知,传统存储路径上从应用层到 ...


注意