C#中的定制特性(Attributes)
AttributesinC#
介绍
Attributes
是一种新的描述信息,我们既可以使用attributes来定义设计期信息(例如帮助文件,文档的URL),还可以用attributes定义运行时信
息(例如,使XML中的元素与类的成员字段关联起来)。我们也可以用attributes来创建一个“自描述”的组件。在这篇指南中我们将明白怎么创建属
性并将其绑定至各种语言元素上,另外我们怎样在运行时环境下获取到attributes的一些信息。
定义
MSDN中做如下定义(ms-help://MS.MSDNQTR.2002APR.1033/csspec/html/vclrfcsharpspec_17_2.htm)
"Anattributeisapieceofadditionaldeclarativeinformationthatisspecifiedforadeclaration."
使用预定义Attributes
在c#中已有一小组预定义的attributes,在我们学习怎样创建自定义attributes前,先来了解下在我们的代码中使用那些预定义的attributes.
usingSystem;
publicclassAnyClass
{ [Obsolete("Don’tuseOldmethod,useNewmethod",true)]
staticvoidOld(){}
staticvoidNew(){}
publicstaticvoidMain() { Old(); } }
|
仔细看下该实例,在该实例中我们用到了”Obsolete”attribute,它标记了一个不该再被使用的语言元素(译者注:这里的元素为方法),该属性的第一个参数是string类型,它解释为什么该元素被荒弃,以及我们该使用什么元素来代替它。实际中,我们可以书写任何其它文本来代替这段文本。第二个参数是告诉编译器把依然使用这被标识的元素视为一种错误,这就意味着编译器会因此而产生一个警告。
当我们试图编译上面的上面的程序,我们会得到如下错误:
AnyClass.Old()' is obsolete: 'Don't use Old method, use New method'
开发自定义Attributes
现在我们即将了解怎么开发自定义的attributes。这儿有个小小处方,有它我们就可以学会创建自定义的attributes。
在C#中,我们的attribute类都派生于System.Attribute类 (A
class that derives from the abstract class System.Attribute, whether
directly or indirectly, is an attribute class. The declaration of an
attribute class defines a new kind of attribute that can be placed on a
declaration) ,我们就这么行动吧。
usingSystem;
publicclassHelpAttribute:Attribute
{
} |
不管你是否相信我,就这样我们就已经创建了一个自定义attribute。现在就可以用它来装饰我们的类了,就像我们使用obsoleteattribute一样。
[Help()]
publicclassAnyClass
{
} |
注意:按惯例我们是用”Attribute“作为attribute类名的后缀,然而,当我们当我们把attribute绑定到某语言元素时,是不包含“Attribute“后缀的。编译器首先在System.Attribute的继承类中查找该attribute,如果没有找到,编译器会把“Attribute“追加到该attribute的名字后面,然后查找它。
但是迄今为止,该attribute没有任何用处。为了使它有点用处,让我们在它里面加点东西吧。
usingSystem; publicclassHelpAttribute:Attribute { publicHelpAttribute(StringDescrition_in) { this.description=Description_in; } protectedStringdescription; publicStringDescription { get { returnthis.description;
} } } [Help("thisisado-nothingclass")] publicclassAnyClass { } |
在上面的例子中,我们在attribute类中添加了一个属性,在最后一节中,我们将在运行时查询该属性。
定义或控制自定义Attribute的用法
AttributeUsage 类是另一预定义类(译者注:attribute类本身用这个atrribute System.AttributeUsage来标记),它将帮助我们控制我们自定义attribute的用法,这就是,我们能为自定义的attribute类定义attributes。
它描述了一个自定义attribute类能被怎样使用。
AttributeUsage 提供三个属性,我们能将它们放置到我们的自定义attribute类上, 第一个特性是:
ValidOn
通过这个属性,我们能指定我们的自定义attribute可以放置在哪些语言元素之上。这组我们能把自定义attribute类放置其上的语言元素被放在枚举器AttributeTargets 中。我们可以使用bitwise(译者注:这个词不知道怎么翻译好,但他的意思是可以这么用:[AttributeUsage((AttributeTargets)4,AllowMultiple=false,Inherited=false)],4代表就是“class”元素,其它诸如1代表“assembly”,16383代表“all”等等)或者”.”操做符绑定几个AttributeTargets 值。(译者注:默认值为AttributeTargets.All)
AllowMultiple
该属性标识我们的自定义attribte能在同一语言元素上使用多次。(译者注:该属性为bool类型,默认值为false,意思就是该自定义attribute在同一语言元素上只能使用一次)
Inherited
我们可以使用该属性来控制我们的自定义attribute类的继承规则。该属性标识我们的自定义attribute是否可以由派生类继承。((译者注:该属性为bool类型,默认值为false,意思是不能继承)
让我们来做点实际的东西吧,我们将把AttributeUsage attribute 放置在我们的help attribute 上并在它的帮助下,我们来控制help attribute的用法。
usingSystem;
[AttributeUsage(AttributeTargets.Class,AllowMultiple=false,Inherited=false)]
publicclassHelpAttribute:Attribute
{
publicHelpAttribute(StringDescription_in)
{
this.description=Description_in;
}
protectedStringdescription;
publicStringDescription
{
get
{
returnthis.description;
}
}
}
|
首先我们注意 AttributeTargets.Class. 它规定这个help attribute 只能放置在语言元素”class”之上。这就意味着,下面的代码将会产生一个错误。
AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
It is valid on 'class' declarations only.
现在试着把它绑定到方法。
[Help("thisisado-nothingclass")]
publicclassAnyClass
{
[Help("thisisado-nothingmethod")]//error
publicvoidAnyMethod()
{
}
}
|
我们可以使用AttributeTargets.All来允许Helpattribute可以放置在任何预定义的语言元素上,那些可能的语言元素如下:
Assembly,
Module,
Class,
Struct,
Enum,
Constructor,
Method,
Property,
Field,
Event,
Interface,
Parameter,
Delegate,
All=Assembly|Module|Class|Struct|Enum|Constructor|Method|Property|Field|Event|Interface|Parameter|Delegate,
ClassMembers=Class|Struct|Enum|Constructor|Method|Property|Field|Event|Delegate|Interface)
~现在考虑下AllowMultiple=false.这就规定该attribute不能在同一语言元素上放置多次.
[Help("thisisado-nothingclass")]
[Help("itcontainsado-nothingmethod")]
publicclassAnyClass
{
[Help("thisisado-nothingmethod")]//error
publicvoidAnyMethod()
{
}
} |
它产生了一个编译错误:
AnyClass.cs: Duplicate 'Help' attribute
Ok!现在我们该讨论下最后那个属性了,”Inherited”, 指出当把该attribute放置于一个基类之上,是否派生类也继承了该attribute。如果绑定至某个attribute类的”Inherited”被设为true,那么该attribute就会被继承,然而如果绑定至某个attribute类的”Inherited”被设为false或者没有定义,那么该attribute就不会被继承。
让我们假设有如下的类关系。
[Help("BaseClass")]
publicclassBase
{
}
publicclassDerive:Base
{
} |
我们有四种可能的绑定:
[AttributeUsage(AttributeTargets.Class,AllowMultiple=false,Inherited=false)]
[AttributeUsage(AttributeTargets.Class,AllowMultiple=true,Inherited=false)]
[AttributeUsage(AttributeTargets.Class,AllowMultiple=false,Inherited=true)]
[AttributeUsage(AttributeTargets.Class,AllowMultiple=true,Inherited=true)]
第一种情况
如果我们查询(我们将在后面来了解如何在运行时来查询attributes)派生类中的helpattribute,我们将不可能查询到因为”Inherited”被设为了false。
第二种情况
第二种情况没有什么不同,因为其”Inherited”也被设为了false。
第三种情况
为了解释第三种和第四种情况,让我们为派生类也绑定同一attribute。
[Help("BaseClass")]
publicclassBase
{
}
[Help("DeriveClass")]
publicclassDerive:Base
{
} |
现在我们查询相关的help attribute ,我们将仅仅可以得到派生类的attribute,为什么这样是因为help attribute虽然允许被继承,但不能多次在同一语言元素上使用,所以基类中的help attribute被派生类的help attribute 重写了。
第四种情况
在第四种情况中,当我们查询派生类的help attribute 时,我们可以得到两个attributes,当然是因为help attribute既允许被继承,又允许在同一语言元素上多次使用的结果。
注意:AttributeUsage attribute 仅应用在那种是System.Attribute 派生的attriubte类而且绑定值该attriubte类的AllowMultiple和Inherited均为false上才是有效的。
可选参数 vs. 命名参数
可选参数是attribute类构造函数的参数。它们是强制的,必须在每次在attribute绑定至某语言元素时提供一个值。而另一方面,命名参数倒是真正的可选参数,不是在attribute构造函数的参数。
为了更加详细的解释,让我们在Help类中添加另外的属性。
[AttributeUsage(AttributeTargets.Class,AllowMultiple=false,
Inherited=false)]
publicclassHelpAttribute:Attribute
{
publicHelpAttribute(StringDescription_in)
{
this.description=Description_in;
this.verion="NoVersionisdefinedforthisclass";
}
protectedStringdescription;
publicStringDescription
{
get
{
returnthis.description;
}
}
protectedStringversion;
publicStringVersion
{
get
{
returnthis.version;
}
//ifweeverwantourattributeusertosetthisproperty,
//wemustspecifysetmethodforit
set
{
this.verion=value;
}
}
}
[Help("ThisisClass1")]
publicclassClass1
{
}
[Help("ThisisClass2",Version="1.0")]
publicclassClass2
{
}
[Help("ThisisClass3",Version="2.0",
Description="Thisisdo-nothingclass")]
publicclassClass3
{
}
|
当我们在Class1中查询Help attribute已经它的属性,我们将得到:
Help.Description : This is Class1
Help.Version :No Version is defined for this class
因为我们没有为Version这个属性定义任何任何值,所以在构造函数中设定的值被我们查询出来了。如果没有定义任何值,那么就会赋一个该类型的默认值(例如:如果是int型,默认值就是0)。
现在,查询Class2的结果是:
Help.Description : This is Class2
Help.Version : 1.0
我们不能为了可选参数而使用多个构造函数,应该用已命名参数来代替。我们之所以称它们为已命名的,是因为当我们在构造函数为它们提供值时,我们必须命名它们。例如,在第二个类中,我们如是定义Help。
[Help("This is Class2", Version = "1.0")]
在 AttributeUsage 例子中, 参数”ValidOn”是可选参数,而“Inherited“和“AllowMultiple“ 是命名参数。
注意:为了在attribute的构造函数中设定命名参数的值,我们必须为相应的属性提供一个set方法否则会引起编译期错误:
'Version' : Named attribute argument can't be a read only property
现在,我们在Class3中查找Help attribute 及其属性会发生什么呢?结果是跟上面提到的相同的编译期错误。
'Desciption' : Named attribute argument can't be a read only property
现在我们修改下Help类,为属性”Description”加一个set方法。现在的输出就是:
Help.Description : This is do-nothing class
Help.Version : 2.0
在屏幕后面究竟发生了什么呢?首先带有可选参数的构造函数被调用,然后,每个命名参数的set方法被调用,在构造函数中赋给命名参数的值被set方法所覆写。
参数类型
一个attribute类的参数类型被限定在如下类型中:
bool,
byte,
char,
double,
float,
int,
long,
short,
string
System.Type
object
Anenumtype,providedthatitandanytypesinwhichitisnestedarepubliclyaccessible.Aone-dimensionalarrayinvolvinganyofthetypeslistedabove
Attributes标记
假设,我们想把Helpattribute绑定至元素assembly。第一个问题是我们要
把Helpattribute放在哪儿才能让编译器确定该attribute是绑定至整个assembly呢?考虑另一种情况,我们想把
attribute绑定至一个方法的返回类型上,怎样才能让编译器确定我们是把attribute绑定至方法的返回类型上,而不是整个方法呢?
为了解决诸如此类的含糊问题,我们使用attribute标识符,有了它的帮助,我们就可以确切地申明我们把attribute绑定至哪一个语言元素。
例如:
[assembly:Help("thisado-nothingassembly")]
这个在Helpattribute前的assembly标识符确切地告诉编译器,该attribute被绑定至整个assembly。可能的标识符有:
assembly
module
type
method
property
event
field
param
return
在运行时查询Attributes
现在我们明白怎么创建attribtes和把它们绑定至语言元素。是时候来学习类的使用者该如何在运行时查询这信息。
为了查询一语言元素上绑定的attributes,我们必须使用反射。反射有能力在运行时发现类型信息。
我们可以使用.NETFrameworkReflectionAPIs通过对整个assembly元数据的迭代,列举出assembly中所有已定义的类,类型,还有方法。
记住那旧的Helpattribute和AnyClass类。
usingSystem;
usingSystem.Reflection;
usingSystem.Diagnostics;
//attachingHelpattributetoentireassembly
[assembly:Help("ThisAssemblydemonstratescustomattributes
creationandtheirrun-timequery.")]
//ourcustomattributeclass
publicclassHelpAttribute:Attribute
{
publicHelpAttribute(StringDescription_in)
{
//
//TODO:Addconstructorlogichere
this.description=Description_in;
//
}
protectedStringdescription;
publicStringDescription
{
get
{
returnthis.deescription;
}
}
}
//attachingHelpattributetoourAnyClass
[HelpString("Thisisado-nothingClass.")]
publicclassAnyClass
{
//attachingHelpattributetoourAnyMethod
[Help("Thisisado-nothingMethod.")]
publicvoidAnyMethod()
{
}
//attachingHelpattributetoourAnyIntField
[Help("ThisisanyInteger.")]
publicintAnyInt;
}
classQueryApp
{
publicstaticvoidMain()
{
}
}
查询程序集的Attributes
在接下来的代码中,我们先得到当前的进程名称,然后用Assembly类中的LoadForm()方法加载程序集,再有用GetCustomAttributes()方法得到被绑定至当前程序集的自定义attributes,接下来用foreach语句遍历所有attributes并试图把每个attribute转型为Help attribute(即将转型的对象使用as关键字有一个优点,就是当转型不合法时,我们将不需担心会抛出异常,代之以空值(null)作为结果),接下来的一行就是检查转型是否有效,及是不是为空,跟着就显示Help attribute的“Description”属性。
classQueryApp
{
publicstaticvoidMain()
{
HelpAttributeHelpAttr;
//QueryingAssemblyAttributes
StringassemblyName;
Processp=Process.GetCurrentProcess();
assemblyName=p.ProcessName+".exe";
Assemblya=Assembly.LoadFrom(assemblyName);
foreach(Attributeattrina.GetCustomAttributes(true)) {
HelpAttr=attrasHelpAttribute;
if(null!=HelpAttr)
{
Console.WriteLine("Descriptionof{0}:/n{1}",
assemblyName,HelpAttr.Description);
}
}
}
} |
程序输出如下:
Description of QueryAttribute.exe:
This Assembly demonstrates custom attributes creation and
their run-time query.
Press any key to continue
查询类、方法、类成员的Attributes
下面的代码中,我们惟一不熟悉的就是Main()方法中的第一行。
Type type = typeof(AnyClass);
它用typeof操作符得到了一个与我们AnyClass类相关联的Type型对象。剩下的查询类attributes代码就与上面的例子是相似的,应该不要解释了吧(我是这么想的)。
为查询方法和类成员的attributes,首先我们得到所有在类中存在的方法和成员,然后我们查询与它们相关的所有attributes,这就跟我们查询类attributes一样的方式。
classQueryApp
{
publicstaticvoidMain()
{
Typetype=typeof(AnyClass);
HelpAttributeHelpAttr;
//QueryingClassAttributes
foreach(Attributeattrintype.GetCustomAttributes(true))
{
HelpAttr=attrasHelpAttribute;
if(null!=HelpAttr)
{
Console.WriteLine("DescriptionofAnyClass:/n{0}",
HelpAttr.Description);
}
}
//QueryingClass-MethodAttributes
foreach(MethodInfomethodintype.GetMethods())
{
foreach(Attributeattrinmethod.GetCustomAttributes(true))
{
HelpAttr=attrasHelpAttribute;
if(null!=HelpAttr)
{
Console.WriteLine("Descriptionof{0}:/n{1}",
method.Name,
HelpAttr.Description);
}
}
}
//QueryingClass-Field(onlypublic)Attributes
foreach(FieldInfofieldintype.GetFields())
{
foreach(Attributeattrinfield.GetCustomAttributes(true))
{
HelpAttr=attrasHelpAttribute;
if(null!=HelpAttr)
{
Console.WriteLine("Descriptionof{0}:/n{1}",
field.Name,HelpAttr.Description);
}
}
}
}
} |
The output of the following program is.
Description of AnyClass:
This is a do-nothing Class.
Description of AnyMethod:
This is a do-nothing Method.
Description of AnyInt:
This is any Integer.
Press any key to continue
|
分享到:
相关推荐
C# 自定义特性 例子
使用自定义特性在unity的inspector面板上显示或者隐藏字段
通过反射获取类的自定义特性,也写了自定义特性类,反射获取类的属性
winform 自定义特性源码! 很值得下载看看!资源免费,大家分享!!
反射和自定义特性DEMO 下了看看对你有帮助!!
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.Entity; using System.Collections;...using System.Diagnostics;... jw = typeof(A).GetCustomAttributes...
CAD VBA开发二次开发资源,能实现查询动态块的自定义特性。
C# 特性 Attribute
c#中关于自定义特性的介绍,主要是用户如何自定义要素,以及使用要素
c# 基于反射、自定义特性、Web Services、xml序列化的应用实例 !
通过反射获得特新内的标注
8月24号做的小例子,创建Attribute子类,并且获得元数据值
。。。
考虑一个简单的C语言项目,我们想让用户决定是否启用一个名为FEATURE_X的自定义特性。如果启用,我们将编译带有特殊行为的代码;如果不启用,我们将编译默认行为的代码。 ———————————————— ...
.Net新手通常容易把属性(Property)跟特性(Attribute)搞混,其实这是两种不同的东西 属性指的类中封装的数据字段;而特性是对类、字段、...特性分预定义特性和自定义特性,本节主要讲述自定义特性 特性能解决什么问
本书适合于C#的各层级的开发人员,内容描述详实,实用,欢迎下载。
不同元素上使用特性、动态发射程序集、多次申明同一特性、多事件类型、反射工厂模式的实现、反射类型的使用、链式委托的返回值、链式委托、句柄...事件模拟场景、事件的示例、委托的实例、一个简单的委托、自定义特性...
自定义特性: UITabBar(高度、背景图片) CenterItem(Image、HighlightedImage、Title) TabBarItem(原图、TitleColor、SelectedTitleColor、TitleFontSize) 普通的Badge(BackgroundColor、ValueColor、FontSize) ...
自定义特性 .NET 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。 创建并使用自定义特性包含四个步骤: 声明自定义特性 构建...
MrCRM 5.0版本是在CRM市场上第一个提出...全部多维报表都可以由客户自定义去实现;自定义字段的统计分析,客户自己添加的字段都可以进入统计分析,并根据业务需求来出具多维统计分析;自定义报表保存为固定报表等特点。