C#中Attribute特性的用法(转载)
2012-09-07 10:37 C#与.NET探索者 阅读(845) 评论(0) 编辑 收藏 举报
Attribute 类将预定义的系统信息或用户定义的自定义信息与目标元素相关联。目标元素可以是程序集、类、构造函数、委托、枚举、事件、字段、接口、方法、可移植可执行文件模块、参数、属性 (Property)、返回值、结构或其他属性 (Attribute)。
属性所提供的信息也称为元数据。元数据可由应用程序在运行时进行检查以控制程序处理数据的方式,也可以由外部工具在运行前检查以控制应用程序处理或维护自身的方式。例如,.NET Framework 预定义属性类型并使用属性类型控制运行时行为,某些编程语言使用属性类型表示 .NET Framework 公共类型系统不直接支持的语言功能。
所有属性类型都直接或间接地从 Attribute 类派生。属性可应用于任何目标元素;多个属性可应用于同一目标元素;并且属性可由从目标元素派生的元素继承。使用 AttributeTargets 类可以指定属性所应用到的目标元素。
C#中一些默认的预定义属性,见下表:
预定义的属性 有效目标 说明
AttributeUsage; Class 指定另一个属性类的有效使用方式
CLSCompliant 全部 指出程序元素是否与CLS兼容
Conditional Method 指出如果没有定义相关联的字符串,编译器可以忽略对这个方法的任何调用
DllImport Method 指定包含外部方法的实现的DLL位置
STAThread Method(Main) 指出程序的默认线程模型为STA
MTAThread Method(Main) 指出程序的默认模型为多线程(MTA)
Obsolete 除了Assembly、Module、Parameter和Return
将一个元素标示为不可用,通知用户此元素将被从未来的产品中移除
ParamArray Parameter 允许单个参数被隐式地当作params(数组)参数对待
Serializable Class、Struct、enum、delegate
指定这种类型的所有公共和私有字段可以被串行化
NonSerialized Field 应用于被标示为可串行化的类的字段,指出这些字段将不可被串行化
StructLayout Class、struct 指定类或结构的数据布局的性质,比如Auto、Explicit或sequential
ThreadStatic Field(静态) 实现线程局部存储(TLS)。不能跨多个线程共享给定的静态字段,每个线程拥有这个静态字段的副本
ComVisibleAttribute 程序集、接口、类、结构、委托、枚举、字段、方法或属性
控制程序集中个别托管类型、成员或所有类型对 COM 的可访问性,默认为 true,指示该托管类型对 COM 是可见的。使所有公共托管程序集及类型可见并不需要使用此属性;默认情况下,它们对 COM 是可见的。只能使 public 类型可见。而不能使用该属性使原本为 internal 或 protected 的类型对 COM 可见,也不能使不可见类型的成员可见。
在程序集上将该属性设置为 false 将隐藏该程序集中的所有 public 类型。通过将个别类型设置为 true,可以有选择地使程序集中的类型可见。如果对于某个特定类型将该属性设置为 false,则将隐藏该类型及其成员。但如果某个类型是不可见的,则无法使该类型的成员可见。如果对于某个类型将该属性设置为 false,则可防止该类型被导出到类型库;不注册类;接口从不响应非托管 QueryInterface 调用。
[ComVisibleAttribute(true)]
DefaultMemberAttribute public DefaultMemberAttribute (String memberName)
memberName 包含要调用的成员名称的 String。这可能是一个构造函数、方法、属性或字段。在调用成员时必须指定合适的调用属性。通过传递一个空 String 作为成员名称,可以指定类的默认成员。
能够通过InvokeMember调用默认成员,而不需要传递调用成员的名称。当需要绑定器但不需要特别的绑定行为时就可以使用它。
下面介绍几种常用的属性
1.[STAThread]和[MTAThread]属性
class Class1{
//使用STAThread属性将程序的默认线程模型指定为单线程模型。注意,线程模型只影响使用COM interop的应用程序,//将这个属性应用于不使用COM interop的程序将不会产生任何效果。
[STAThread]
Static void Main( string[] args ){
}
}
2. AttributeUsage属性
3.Conditional 属性:
这个属性附着于方法,这样当编译器遇到对这个方法调用时,如果没有定义对应的字符串值,编译器就忽略这个调用。例如,以下方法是否被编译取决于是否定义了字符串“DEGUG”:
#define DEBUG
#undef DEBUG
[Condition(“DEBUG”) ]
public void SomeDebugFunc() {
Console.WriteLine(“SomeDebugFunc”);
}
4. Obsolete 属性 [Obsolete("Don't use OldFunc, use NewFunc instead", true)] 标记一个语言元素被废止
5. DllImport和StructLayout属性
DllImport可以让C#代码调用本机代码中的函数,C#代码通过平台调用(platform invoke)这个运行时功能调用它们。
StructLayout:公共语言运行库利用StructLayoutAttribute控制类或结构的数据字段在托管 内存中的物理布局,即类或结构需要按某种方式排列。如果要将类传递给需要指定布局的非托管代码,则显式控制类布局是重要的。它的构造函数中用 LayoutKind值初始化 StructLayoutAttribute 类的新实例。 LayoutKind.Sequential 用于强制将成员按其出现的顺序进行顺序布局。
LayoutKind.Sequential 顺序布局 出现的每一个字段,在内存里按出现顺序依次排列
LayoutKind. Explicit 精确布局 需要用FieldOffset()设置每个成员的位置,这样就可以实现类似c的公用体的功能
struct S1
{
[FieldOffset(0)] //这样a和b在内存中地址相同
int a;
[FieldOffset(0)] //这样a和b在内存中地址相同
int b;
}
using System;
using System.Runtime.InteropServices; // for DllImport
namespace nativeDLL
{
publicclass Test
{
//[DllImport ("user32.dll")] // all the defaults are OK
[DllImport("user32", EntryPoint="MessageBoxA",SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true,CallingC.StdCall)]
publicstaticexternint MessageBoxA (int h, string m, string c, int type);
[DllImport ("kernel32.dll")]
publicstaticexternvoid GetLocalTime(SystemTime st);
[StructLayout(LayoutKind.Sequential)] //
publicclass SystemTime {
publicushort wYear;
publicushort wMonth;
publicushort wDayOfWeek;
publicushort wDay;
publicushort wHour;
publicushort wMinute;
publicushort wSecond;
publicushort wMilliseconds;
}
[STAThread]
publicstaticvoid Main(string[] args)
{
MessageBoxA(0, "Hello World", "nativeDLL", 0);
SystemTime st =new SystemTime();
GetLocalTime(st);
string s = String.Format("date: {0}-{1}-{2}",st.wMonth, st.wDay, st.wYear);
string t = String.Format("time: {0}:{1}:{2}", st.wHour, st.wMinute, st.wSecond);
MessageBoxA(0, s +", "+ t, "Now", 0);
}
}
}
6. 配件属性
当使用.NET产生任何类型的C#工程时,会自动的产生一个AssemblyInfo.cs源代码文件以及应用程序源代码文件。 AssemblyInfo.cs中含有配件中代码的信息。其中的一些信息纯粹是信息,而其它信息使运行时环境可以确保惟一的命名和版本号,以供重用你的配件的客户代码使用。
7. 上下文属性
.NET柜架还提供了另一种属性:上下文属性。上下文属性提供了一种截取机制,可以在类的实例化和方法调用之前和之后进行处理。这种功能用于对象远程调用,它是从基于COM的系统所用的COM+组件服务和Microsoft Transaction Services(MTS)。
使用c#中预定义 Attribute
开发自定义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)
using System;
|
注意:按惯例我们是用”Attribute“作为attribute类名的后缀,然而,当我们当我们把attribute绑定到某语言元素时,是不包含“Attribute“后缀的。编译器首先在System.Attribute 的继承类中查找该attribute,如果没有找到,编译器会把“Attribute“追加到该attribute的名字后面,然后查找它。
在上面的例子中,我们在attribute类中添加了一个属性,在最后一节中,我们将在运行时查询该属性。
定义或控制自定义Attribute的用法
AttributeUsage 类是另一预定义类(attribute类本身用这个atrribute System.AttributeUsage来标记),它提供三个属性帮助我们控制我们自定义attribute的用法,
ValidOn :(在哪些元素上有效),值为AttributeTargets的枚举,支持bitwise(按位计算)
[AttributeUsage(AttributeTargets.Class, AllowMultiple =false, Inherited =false)]
publicclass HelpAttribute : Attribute {
public HelpAttribute(String Description_in){
this.description = Description_in;
}
protected String description;
public String Description{ get{ returnthis.description;} }
}
AllowMultiple:是否允许在同一语言元素上使用多次,默认为false ,下面将产生编译错误
[Help("it contains a do-nothing method")] //AnyClass.cs: Duplicate(复制) 'Help' attribute
publicclass AnyClass{
[Help("this is a do-nothing method")] //error
publicvoid AnyMethod(){
}
}
Inherited:当我们在基类上使用我们的自定义Attribute时,基类的派生类是否也继承基类上的Attribute,默认false。
Ok!现在我们该讨论下最后那个属性了,”Inherited”, 指出当把该attribute放置于一个基类之上,是否派生类也继承了该attribute。如果绑定至某个attribute类的”Inherited”被设为true,那么该attribute就会被继承,然而如果绑定至某个attribute类的”Inherited”被设为false或者没有定义,那么该attribute就不会被继承。
让我们假设有如下的类关系。
[Help("BaseClass")] public class Base{ }
public class Derive : Base{ } |
我们有四种可能的绑定:
[AttributeUsage(AttributeTargets.Class, AllowMultiple =false, Inherited =false )]
[AttributeUsage(AttributeTargets.Class, AllowMultiple =true, Inherited =false) ]
//虽然允许被继承,若派生类中也定义该属性将重写基类的属性,因为不能多次在同一语言元素上多次使用
[AttributeUsage(AttributeTargets.Class, AllowMultiple =false, Inherited =true )]
//若派生类也定义了自己的Attribute,查询时将得到包含基类Attribute在内的两个Attribute的实例
[AttributeUsage(AttributeTargets.Class, AllowMultiple =true, Inherited =true) ]
可选参数 vs命名参数 源于Attribute构造函数的不同签名
可选参数:使用该Attribute时必须提供值的参数,
命名参数:使用该Attribute可以不提供值,使用Attribute默认值的参数
参数类型: 一个attribute类的参数类型被限定在如下类型中
bool, byte, char, double, float, int, long, short, string, System.Type, object,
An enum type, provided that it and any types in which it is nested are publicly accessible.
A one-dimensional array involving any of the types listed above //一位数组
Attribute
Attribute 标识符
可能的标识符:assembly module type method property event field param return
假设我们想把HelpAttribute绑定至元素 assembly。
第一个问题:我们要把Help attribute 放在哪儿才能让编译器确定该attribute是绑定至整个assembly呢?
第二个问题:我们想把attribute绑定至一个方法的返回类型上,怎样才能让编译器确定我们是把attribute绑定至方法的返回类型上,而不是整个方法呢?
[assembly: Help("something")] //该attribute被绑定至整个assembly
[module: Help("something ")]
[type: Help("something ")]
[method: Help("something ")]
[property: Help("something ")]
[field: Help("something ")]
[param: Help("something ")]
[return: Help("something ")]
在运行时查询Attribute Attribute的信息绑定在类所属的Type类型中
string assemblyFileName = Process.GetCurrentProcess().ProcessName +".exe";
Assembly assembly = Assembly.LoadFrom(assemblyFileName);
HelpAttribute helpAttribute;
foreach (Attribute attribute in assembly.GetCustomAttributes(true))
{
helpAttribute = attribute as HelpAttribute;
if (helpAttribute !=null){
string description = helpAttribute.Description;
}
}
}
1. 查询类、方法、类成员的Attribute
首先应获得与相应与成员相关的类型(Type)对象,成员的Attribute信息都绑定在Type对象中
{
HelpAttribute helpAttribute;
Type classType =this.GetType();
//类的Attribute信息
foreach (Attribute attribute in classType.GetCustomAttributes(true))
{
if (attribute !=null&& (attribute is HelpAttribute))
{
helpAttribute = attribute as HelpAttribute;
string description = helpAttribute.Description;
}
}
//类的方法的Attribute信息
foreach (MethodInfo methodInfo in classType.GetMethods())
{
//methodInfo.Invoke("Object obj:对其调用方法或构造函数的对象,如果方法是静态的,则为null"
, "object[] parameters");
foreach (Attribute attribute in methodInfo.GetCustomAttributes(true))
{
if (attribute !=null&& (attribute is HelpAttribute))
{
helpAttribute = attribute as HelpAttribute;
string description = helpAttribute.Description;
}
}
}
//类的public的Field的Attribute信息
foreach (FieldInfo fieldInfo in classType.GetFields())
{
foreach (Attribute attribute in fieldInfo.GetCustomAttributes(true))
{
if (attribute !=null&& (attribute is HelpAttribute))
{
helpAttribute = attribute as HelpAttribute;
string description = helpAttribute.Description;
}
}
}
}
{
//标记了一个不该再被使用的语言元素:此为方法,同时把依然使用此方法的元素标记为错误,编译器产生警告
[Obsolete("Don’t use Old method, use New method", true)]
staticvoid Old( ) { }
staticvoid New( ) { }
publicstaticvoid Main( )
{
Old( );
}
}
例子:class Program
{
static void Main(string[] args)
{
Demo d=new Demo();
string userName="Lucy";
MethodInfo mi=d.GetType().GetMethod("Test");
if (mi == null) return;
AllowExecuteAttribute att=Attribute.GetCustomAttribute(mi, typeof(AllowExecuteAttribute)) as AllowExecuteAttribute;
if (att == null) return;
if (att.Check(userName))
Console.WriteLine("允许执行");
else
Console.WriteLine("不允许执行");
}
}
class Demo
{
[AllowExecute("Jack, Tom")]
public void Test() { }
}
/// <summary>
/// 标识某方法允许执行的用户
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class AllowExecuteAttribute : Attribute
{
/// <summary>
///
/// </summary>
/// <param name="allowedUsers">允许执行的用户名的串联字符串</param>
public AllowExecuteAttribute(string allowedUsers)
{
this._allowedUsers = allowedUsers;
}
private string _allowedUsers;
public bool Check(string userName)
{
return this._allowedUsers.ToLower().IndexOf(userName.ToLower()) > -1;
}
}