C#特性详解
特性提供功能强大的方法用以将元数据或声明信息与代码(程序集类型方法属性等)相关联特性与程序实体关联后即可在运行时使用名为反射的技术查询特性这篇文章绝大部分是按照MSDN来学习的只是加了一点点自己的东东官方介绍的很详细我们就一起来了解一下它的用法
特性具有以下属性
特性可向程序中添加元数据元数据是有关在程序中定义的类型的信息所有的 NET 程序集都包含指定的一组元数据这些元数据描述在程序集中定义的类型和类型成员可以添加自定义特性以指定所需的任何附加信息
可以将一个或多个特性应用到整个程序集模块或较小的程序元素(如类和属性)
特性可以与方法和属性相同的方式接受参数
程序可以使用反射检查自己的元数据或其他程序内的元数据
这些都是官方的定义那么对于一个初学者来说看的懂汉字不难但是上面的元数据是什么?
我这么通俗的解释下
你注意过程序及编译的时候的pdb文件了吗?pdb文件里面存储了关于程序集内部的所有的成员信息例如成员的数据类型属性类型方法返回值方法入参类型就是程序及内部所有的定义信息的数据信息是存储定义信息的一类数据信息程序集里面的所有的关于声明类的数据信息包括方法间调用都是存储在元数据里面
下面开始一同学习特性的用法
特性可以放置在几乎所有的声明中在 C# 中特性的指定方法为将括在方括号中的特性名置于其应用到的实体的声明上方
[SystemSerializable]
public class SampleClass
{
// Objects of this type can be serialized
}
一个声明上可放置多个特性
using SystemRuntimeInteropServices;
…
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(TEST)]
void TraceMethod()
{
// …
}
根据约定所有特性名称都以单词Attribute结束以便将它们与NET Framework中的其他项区分但是在代码中使用特性时不需要指定 attribute 后缀例如[DllImport] 虽等效于 [DllImportAttribute]但 DllImportAttribute 才是该特性在 NET Framework 中的实际名称
特性参数
许多特性都有参数而这些参数可以是定位参数未命名参数或命名参数任何定位参数都必须按特定顺序指定并且不能省略而命名参数是可选的且可以按任意顺序指定首先指定定位参数例如这三个特性是等效的
[DllImport(userdll)]
[DllImport(userdll SetLastError=false ExactSpelling=false)]
[DllImport(userdll ExactSpelling=false SetLastError=false)]
第一个参数(DLL 名称)是定位参数并且总是第一个出现其他参数为命名参数在这种情况下两个命名参数均默认为 false因此可将其省略有关默认参数值的信息可以参考参考各个特性的文档
特性目标
特性的目标是应用该特性的实体例如特性可以应用于类特定方法或整个程序集默认情况下特性应用于它后面的元素但是您也可以显式标识要将特性应用于方法还是它的参数或返回值
若要显式标识特性目标请使用下面的语法
[target : attributelist]
下表显示了可能的 target 值的列表
C# dataguid=aacecbcfbeeadceee>C# | 适用对象 | assembly
整个程序集
module
当前程序集模块(不同于 Visual Basic 模块)
field
在类或结构中的字段
event
Event
method
get and set property accessors dataguid=cdefbdffcfdfddc>方法或 get 和 set 属性访问器
param
set property accessor parameters dataguid=eebcbbdbc>方法参数或 set 属性访问器参数
property
Property
return
get property accessor dataguid=cdeaaffbecbaba>方法属性索引器或 get 属性访问器的返回值
type
结构类接口枚举或委托
下面的示例演示如何将特性应用于程序集和模块
using System;
using SystemReflection;
[assembly: AssemblyTitleAttribute(Production assembly )]
[module: CLSCompliant(true)]
下面的示例演示如何在 C# 中将特性应用于方法方法参数和方法返回值
// default: applies to method
[SomeAttr]
int Method() { return ; }
// applies to method
[method: SomeAttr]
int Method() { return ; }
// applies to return value
[return: SomeAttr]
int Method() { return ; }
无论规定 SomeAttr 应用于什么目标都必须指定 return 目标即使 SomeAttr 被定义为仅应用于返回值也是如此换言之编译器将不使用 AttributeUsage 信息解析不明确的特性目标
呀终于了解完了是时候自己十一下手了我们就动手活动一下颈骨多加点注释来关联一下学过的内容
我们这里拿ObsoleteAttribute做下测试它标记不再使用的程序元素此类不能被继承首先我们看一下它的继承结构
src=http://imgeducitycn/img_///png>
当然我们看看其他的特性我们就会发现特性其实是从SystemObject类派生出来的一种特殊类
我们现在用这个构造来验证
public ObsoleteAttribute(string message bool error)
参数类型
message System ::String描述可选的变通方法的文本字符串
errorSystem ::Boolean指示是否将使用已过时的元素视为错误的布尔值
总之我们在使用特性的时候不要产生畏惧就当他是特殊的类以前怎么样用构造函数现在仍旧怎么用只是格式有点微妙的变化
using System;
namespace 特性
{
class Program
{
static void Main(string[] args)
{
OldClass old = new OldClass()//个报错因为使用OldClass两次 oldOldMethod()//警告因为第二个参数未指定使用已过时的元素不会视为错误
ConsoleReadKey()
}
}
[Obsolete(该类已经过时true)]//使用默认的特性目标直接作用于紧随其后的Class OldClass
//第二个参数我这里设置为true将使用已过时的元素视为错误
class OldClass
{
[method: Obsolete(该方法已经过时)]
public void OldMethod()
{
ConsoleWriteLine(过时的方法!)
}
}
}
运行以后会出现两个错误提示一个警告提示
src=http://imgeducitycn/img_///png>
好了现在我们在紧接着学习自定义特性这个估计就算是相当简单了
自定义特性
通过定义一个特性类可以创建您自己的自定义特性该特性类直接或间接地从Attribute派生有助于方便快捷地在元数据中标识特性定义假设您要用编写类型的程序员的名字标记类型可以定义一个自定义 Author特性类
[SystemAttributeUsage(SystemAttributeTargetsClass |
SystemAttributeTargetsStruct)
]
public class Author : SystemAttribute
{
private string name;
public double version;
public Author(string name)
{
thisname = name;
version = ;
}
}
类名是特性的名称即 Author它由 SystemAttribute 派生而来因此是自定义特性类构造函数的参数是自定义特性的定位参数本示例中 name 是定位参数任何公共的读写字段或属性都是命名参数在本例中version 是唯一的命名参数请注意 AttributeUsage 特性的用法它使得 Author 特性仅在类声明中有效
可以按如下所示使用此新特性
[Author(P Ackerman version = )]
class SampleClass
{
// P Ackermans code goes here…
}
AttributeUsage 有一个命名参数 AllowMultiple使用它可以使自定义特性成为一次性使用或可以使用多次的特性在下面的代码示例中创建了一个使用多次的特性
[SystemAttributeUsage(SystemAttributeTargetsClass |
SystemAttributeTargetsStruct
AllowMultiple = true) // multiuse attribute
]
public class Author : SystemAttribute
在下面的代码示例中向某个类应用了同一类型的多个特性
[Author(P Ackerman version = )]
[Author(R Koch version = )]
class SampleClass
{
// P Ackermans code goes here…
// R Kochs code goes here…
}
如果特性类包含一个属性则该属性必须为读写属性
介绍完了官方的示例是不是还是云里雾里那么我们一起来深入解剖一下
首先我们从上面可以总结出创建自定义特性的大概步骤
应用AttributeUsage特性 虽然等效但AttributeUsageAttribute 才是该特性在 NET Framework 中的实际名称它也是由 SystemAttribute 派生而来
声明特性类它由 SystemAttribute 派生而来
声明构造函数
声明特性
OVER!!!就这么回事完了吗我们继续剖析之重要的信息AttributeUsage特性
AttributeUsage特性研究特性当然首要的要研究其构造函数现在我们来看看他是怎么定义的
public AttributeUsageAttribute( AttributeTargets validOn)
参数 validOn 类型SystemAttributeTargets 使用按位或运算符组合的一组值用于指示哪些程序元素是有效的
用指定的 AttributeTargetsAllowMultiple 值和 Inherited 值列表初始化 AttributeUsageAttribute 类的新实例
于是乎我们返回到了研究AttributeTargets的问题了现在我们就来细心的看看他是神马!
原来他是一个枚举通过该特性可使其成员值按位组合可以通过按位或运算组合 AttributeTargets 枚举值来获得首选组合
成员
| 成员名称 | 说明 | 由 XNA Framework 提供支持
alt=
由 XNA Framework 提供支持
src=
http://img
educity
cn/img_
/
/
/
gif
>
fs
PortableClassLibrary(zh
cn
VS
)
gif alt=fa
fs
PortableClassLibrary(zh
cn
VS
)
gif src=
http://img
educity
cn/img_
/
/
/
gif
>Assembly可以对程序集应用特性
由 XNA Framework 提供支持
alt=
由 XNA Framework 提供支持
src=
http://img
educity
cn/img_
/
/
/
gif
>
fs
PortableClassLibrary(zh
cn
VS
)
gif alt=fa
fs
PortableClassLibrary(zh
cn
VS
)
gif src=
http://img
educity
cn/img_
/
/
/
gif
>Module可以对模块应用特性
http://imgeducitycn/img_///gif>注意
Module refers to a portable executable file (dll orexe) and not a Visual Basic standard module dataguid=ccbbfdeaefc>Module 指的是可移植的可执行文件(dll 或 exe)而非 Visual Basic 标准模块由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Class可以对类应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Struct可以对结构应用特性即值类型由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Enum可以对枚举应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Constructor可以对构造函数应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Method可以对方法应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Property可以对属性应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Field可以对字段应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Event可以对事件应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Interface可以对接口应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Parameter可以对参数应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>Delegate可以对委托应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>ReturnValue可以对返回值应用特性由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>GenericParameter可以对泛型参数应用特性
http://imgeducitycn/img_///gif>注意
目前此特性可以应用仅于 C#Microsoft 中间语言 (MSIL) 和发出的代码由 XNA Framework 提供支持 alt=由 XNA Framework 提供支持 src=http://imgeducitycn/img_///gif>fsPortableClassLibrary(zhcnVS)gif alt=fafsPortableClassLibrary(zhcnVS)gif src=http://imgeducitycn/img_///gif>All可以对任何应用程序元素应用特性
到了这里一节也就明了了谜底都一一展现在我们的面前
按照上面的经验再次开始动手来熟悉这一切我指定了该自定义的特性不可继承就在不解释别的了只是为了证明一下命名参数Inherited定性成功与否总之还是很简单的
? cellPadding= border=>
csharp keyword>using
csharp plain>System;
csharp spaces>
csharp keyword>namespace
csharp plain>特性
csharp plain>{
csharp spaces>
csharp keyword>class
csharp plain>Program
csharp spaces>
csharp plain>{
csharp spaces>
csharp keyword>static
csharp keyword>void
csharp plain>Main(
csharp keyword>string
csharp plain>[] args)
csharp spaces>
csharp plain>{
csharp spaces>
csharp plain>GetAttributeInfo(
csharp keyword>typeof
csharp plain>(OldClass));
csharp spaces>
csharp plain>ConsoleWriteLine(
csharp string>==============
csharp plain>);
csharp spaces>
csharp plain>GetAttributeInfo(
csharp keyword>typeof
csharp plain>(NewClass));
csharp spaces>
csharp plain>ConsoleReadKey();
csharp spaces>
csharp plain>}
csharp spaces>
csharp keyword>public
csharp keyword>static
csharp keyword>void
csharp plain>GetAttributeInfo(Type t)
csharp spaces>
csharp plain>{
csharp spaces>
csharp plain>OldAttribute myattribute = (OldAttribute)AttributeGetCustomAttribute(t
csharp keyword>typeof
csharp plain>(OldAttribute));
csharp spaces>
csharp keyword>if
csharp plain>(myattribute ==
csharp keyword>null
csharp plain>)
csharp spaces>
csharp plain>{
csharp spaces>
csharp plain>ConsoleWriteLine(tToString()+
csharp string>类中自定义特性不存在!
csharp plain>);
csharp spaces>
csharp plain>}
csharp spaces>
csharp keyword>else
csharp spaces>
csharp plain>{
csharp spaces>
csharp plain>ConsoleWriteLine(
csharp string>特性描述:{}\n加入事件{}
csharp plain> myattributeDiscretion myattributedate);
csharp spaces>
csharp plain>}
csharp spaces>
csharp plain>}
csharp spaces>
csharp plain>}
csharp spaces>
csharp spaces>
csharp plain>[AttributeUsage(AttributeTargetsClassInherited=
csharp keyword>false
csharp plain>)]
csharp comments>//设置了定位参数和命名参数
csharp spaces>
csharp spaces>
csharp comments>//该特性适用于所有的类而且是非继承的
csharp spaces>
csharp keyword>class
csharp plain>OldAttribute : Attribute
csharp comments>//继承自Attribute
csharp spaces>
csharp plain>{
csharp spaces>
csharp keyword>private
csharp keyword>string
csharp plain>discretion;
csharp spaces>
csharp spaces>
csharp keyword>public
csharp keyword>string
csharp plain>Discretion
csharp spaces>
csharp plain>{
csharp spaces>
csharp keyword>get
csharp plain>{
csharp keyword>return
csharp plain>discretion; }
csharp spaces>
csharp keyword>set
csharp plain>{ discretion = value; }
csharp spaces>
csharp plain>}
csharp spaces>
csharp keyword>public
csharp plain>DateTime date;
csharp spaces>
csharp keyword>public
csharp plain>OldAttribute(
csharp keyword>string
csharp plain>discretion)
csharp spaces>
csharp plain>{
csharp spaces>
csharp keyword>this
csharp plain>discretion = discretion;
csharp spaces>
csharp plain>date = DateTimeNow;
csharp spaces>
csharp plain>}
csharp spaces>
csharp plain>}
csharp spaces>
csharp comments>//现在我们定义两类
csharp spaces>
csharp plain>[Old(
csharp string>这个类将过期
csharp plain>)]
csharp comments>//使用定义的新特性
csharp spaces>
csharp keyword>class
csharp plain>OldClass
csharp spaces>
csharp plain>{
csharp spaces>
csharp keyword>public
csharp keyword>void
csharp plain>OldTest()
csharp spaces>
csharp plain>{
csharp spaces>
csharp plain>ConsoleWriteLine(
csharp string>测试特性
csharp plain>);
csharp spaces>
csharp plain>}
csharp spaces>
csharp plain>}
csharp spaces>
csharp keyword>class
csharp plain>NewClass:OldClass
csharp spaces>
csharp plain>{
csharp spaces>
csharp keyword>public
csharp keyword>void
csharp plain>NewTest()
csharp spaces>
csharp plain>{
csharp spaces>
csharp plain>ConsoleWriteLine(
csharp string>测试特性的继承
csharp plain>);
csharp spaces>
csharp plain>}
csharp spaces>
csharp plain>}
csharp spaces>
csharp comments>//我们写一个方法用来获取特性信息
csharp plain>}
运行效果
src=http://imgeducitycn/img_///png>