[CLR via C#]18. Attribute
- [assembly: MyAttr()] // 应用于程序集
- [module: MyAttr()] // 应用于模块
- [type: MyAttr()] // 应用于类型
- internal sealed class SomeType<[typevar: MyAttr()] T> { // 应用于泛型类型变量
- [field: MyAttr()] // 应用于字段
- public Int32 SomeField = ;
- [return: MyAttr()] // 应用于返回值
- [method: MyAttr()] // 应用于方法
- public Int32 SomeMethod(
- [param: MyAttr()] // 应用于方法参数
- Int32 SomeParam) { return SomeParam; }
- [property: MyAttr()] // 应用于属性
- public String SomeProp {
- [method: MyAttr()] // 应用于get访问器方法
- get { return null; }
- }
- [event: MyAttr()] // 应用于事件
- [field: MyAttr()] // 应用于编译器生成的字段
- [method: MyAttr()] // 应用于编译器生成的add&&remove方法
- public event EventHandler SomeEvent;
- }
- [AttributeUsage(AttributeTargets.All)]
- public class MyAttr : Attribute {
- public MyAttr(Int32 x) { }
- }
前面介绍了如何应用一个attribute,接下来看看attribute到底是什么?
attribute实际是一个类的实例。为了符合"公共语言规范"(CLS)的要求,attribute类必须直接或间接地从公共抽象类System.Attribute派生。C#只允许使用符合CLS规范的attribute。attribute类是可以在任何命名空间中定义的。
- [DllImport("kernel32",CharSet = CharSet.Auto, SetLastError = true)]
这一行代码语法表面上很奇怪,因为调用构造器时永远不会出现这样的语法。查阅DllImportAttbute类的文档,会发现它只接受一个String类型的参数。在这个例子中,"Kernel32"这个String类型的参数已经传给它了。构造器的参数称为"定位参数",而且是强制性的。也就是说,应用attribute时,必须指定参数。
- public enum Color { Red }
- [AttributeUsage(AttributeTargets.All)]
- internal sealed class SomeAttribute : Attribute
- {
- public SomeAttribute(String name, Object o, Type[] types)
- {
- // 'name' 引用了一个String类型
- // 'o' 引用了一个合法类型(如有必要,就进行装箱)
- // 'types' 引用一个一维0基Type数组
- }
- }
- [Some("Jeff", Color.Red, new Type[] { typeof(Math), typeof(Console) })]
- internal sealed class SomeType
- {
- }
逻辑上,当编译器检测到一个目标元素应用了一个attribute时,编译器会调用attribute类的构造器,向它传递任何指定的参数,从而构造attribute类的一个实例。然后,编译器会采用增强型构造器语法所指定的值,对任何公共字段和属性进行初始化。在构造并初始化好定制attribute类的对象之后,编译器会将这个attribute对象序列化到目标元素的元数据表记录项中。
- public static String Format(Type enumType, Object value, String format) {
- // 枚举类型是否应用了FlagsAttrobute类型的一个实例
- if(enumType.IsDefined(typeof(FlagsAttribute),false)){
- //如果是,就执行代码,将值视为一个位标志枚举类型
- ...
- }else {
- // 如果不是,就执行代码,将值视为一个普通枚举类型
- }
- }
上述代码调用Type的IsDefined方法,要求系统查看枚举类型的元数据,检查是否关联了FlagsAttribute类的一个实例。如果IsDefined返回true,表面FlagsAttribute的一个实例已于枚举类型关联,Format方法会认为值包含了一个位标志集合。如果IsDefined放回false,Format方法会认为值是一个普通的枚举类型。
方法名称 | 说明 |
IsDefined | 如果至少有一个指定的Attribute派生类的实例如目标关联,就放回true。这个方法效率很高,因为它不构造(反序列化)attribute类的任何实例 |
GetCustomAttribute |
返回引用于目标的指定attribute类的一个实例。实例使用编译时指定的参数、字段和属性来构造。如果目标没有引用任何attribute类的实例,就返回null。如果目标应用了指定attribute的多个实例,就抛出异常。方法通常用于已将AllowMultiple设为false的attribute。
|
GetCustomAttributes | 返回一个数组,其中每个元素都是应用于目标的指定attribute类的一个实例。如果不为这个方法指定具体的attribute类,数组中包含的就是已应用的所有attribute的实例,不管它们是什么类。每个实例都使用编译时指定的参数、字段和属性来构造(反序列化)。如果目标没有应用任何attribute类的实例,就返回一个空数组。该方法通常用于已将AllowMultiple设为true的attribute,或者用于列出已应用的所有attribute。 |
- [assembly: CLSCompliant(true)]
- [Serializable]
- [DefaultMemberAttribute("Main")]
- [DebuggerDisplayAttribute("Richter", Name = "Jeff", Target = typeof(Program))]
- public sealed class Program
- {
- [Conditional("Debug")]
- [Conditional("Release")]
- public void DoSomething() { }
- public Program()
- {
- }
- [assembly: CLSCompliant(true)]
- [STAThread]
- public static void Main()
- {
- // 显示应用于这个类型的attribute集
- ShowAttributes(typeof(Program));
- // 获取与类型关联的方法集
- MemberInfo[] members = typeof(Program).FindMembers(
- MemberTypes.Constructor | MemberTypes.Method,
- BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static,
- Type.FilterName, "*");
- foreach (MemberInfo member in members)
- {
- // 显示应用于这个成员的attribute集
- ShowAttributes(member);
- }
- Console.Read();
- }
- private static void ShowAttributes(MemberInfo attributeTarget)
- {
- Attribute[] attributes = Attribute.GetCustomAttributes(attributeTarget);
- Console.WriteLine("Attributes applied to {0}: {1}",
- attributeTarget.Name, (attributes.Length == ? "None" : String.Empty));
- foreach (Attribute attribute in attributes)
- {
- // 显示已应用的每个attribute的类型
- Console.WriteLine(" {0}", attribute.GetType().ToString());
- if (attribute is DefaultMemberAttribute)
- Console.WriteLine(" MemberName={0}",
- ((DefaultMemberAttribute)attribute).MemberName);
- if (attribute is ConditionalAttribute)
- Console.WriteLine(" ConditionString={0}",
- ((ConditionalAttribute)attribute).ConditionString);
- if (attribute is CLSCompliantAttribute)
- Console.WriteLine(" IsCompliant={0}",
- ((CLSCompliantAttribute)attribute).IsCompliant);
- DebuggerDisplayAttribute dda = attribute as DebuggerDisplayAttribute;
- if (dda != null)
- {
- Console.WriteLine(" Value={0}, Name={1}, Target={2}",
- dda.Value, dda.Name, dda.Target);
- }
- }
- Console.WriteLine();
- }
- }
输出结果为:
- [Flags]
- internal enum Accounts
- {
- Savings = 0x0001,
- Checking = 0x0002,
- Brokerage = 0x0004
- }
- [AttributeUsage(AttributeTargets.Class)]
- internal sealed class AccountsAttribute : Attribute
- {
- private Accounts m_accounts;
- public AccountsAttribute(Accounts accounts)
- {
- m_accounts = accounts;
- }
- public override Boolean Match(Object obj)
- {
- // 如果基类实现了Match,而且基类
- // 不是Attribute,就取消对下面这行的注释
- //if (!base.Match(obj))
- //{
- // return false;
- //}
- if (obj == null)
- {
- return false;
- }
- if (this.GetType() != obj.GetType())
- {
- return false;
- }
- AccountsAttribute other = (AccountsAttribute)obj;
- // 比较字段,判断它们是否有相同的值
- if ((other.m_accounts & m_accounts) != m_accounts)
- {
- return false;
- }
- return true; // 对象匹配
- }
- public override Boolean Equals(Object obj)
- {
- //如果基类实现了Equals,而且基类不能Object
- //就取消对下面的注释
- //if (!base.Equals(obj))
- //{
- // return false;
- //}
- if (obj == null)
- {
- return false;
- }
- if (this.GetType() != obj.GetType())
- {
- return false;
- }
- AccountsAttribute other = (AccountsAttribute)obj;
- // 比较字段,判断它们是否有相同的值
- if (other.m_accounts != m_accounts)
- {
- return false;
- }
- return true; // Objects are equal
- }
- // 还需要重写GetHashCode,因为我们重写了Equals
- public override Int32 GetHashCode()
- {
- return (Int32)m_accounts;
- }
- }
- [Accounts(Accounts.Savings)]
- internal sealed class ChildAccount { }
- [Accounts(Accounts.Savings | Accounts.Checking | Accounts.Brokerage)]
- internal sealed class AdultAccount { }
- public class Program
- {
- public static void Main()
- {
- CanWriteCheck(new ChildAccount());
- CanWriteCheck(new AdultAccount());
- // 只是为了演示在一个没有应用AccountsAttribute的方法也能正确工作
- CanWriteCheck(new Program());
- Console.Read();
- }
- private static void CanWriteCheck(Object obj)
- {
- // 构造attribute类型的一个实例,并把它初始化我们要
- // 显示查找的内容
- Attribute checking = new AccountsAttribute(Accounts.Checking);
- // 构造应用于类型的attribute实例
- Attribute validAccounts = Attribute.GetCustomAttribute(
- obj.GetType(), typeof(AccountsAttribute), false);
- // 如果attribute应用于类型,而且attribute指定了
- // "Checking" 账户, 表面该类可以开支票
- if ((validAccounts != null) && checking.Match(validAccounts))
- {
- Console.WriteLine("{0} types can write checks.", obj.GetType());
- }
- else
- {
- Console.WriteLine("{0} types can NOT write checks.", obj.GetType());
- }
- }
- }
输出结果:
- //#define TEST
- #define VERIFY
- namespace ConsoleTest
- {
- using System;
- using System.Diagnostics;
- [Conditional("TEST")]
- [Conditional("VERIFY")]
- public sealed class CondAttribute : Attribute
- {
- }
- [Cond]
- public class Program
- {
- public static void Main()
- {
- Console.WriteLine("CondAttribute is {0} applied to Program type.",
- Attribute.IsDefined(typeof (Program), typeof (CondAttribute)) ? "" : "not");
- Console.Read();
- }
- }
- }
编译器如果发现向目标元素应用了CondAttribute的一个实例,那么当含有目标元素的代码编译时,只有定义TEST或VERIFY符号的前提下,编译器才会在元数据中生成attribute信息。虽然如此,attribute类的定义元数据和实现存在于程序集中。
[CLR via C#]18. Attribute的更多相关文章
- CLR via C#(18)——Enum
1. Enum定义 枚举类型是经常用的一种“名称/值”的形式,例如: public enum FeedbackStatus { New, Processing, ...
- 一百多道.NET面试题!
1.a=10,b=15,在不用第三方变量的前提下,把a,b的值互换 方法一: a=a+b; b=a-b; a=a-b; 方法二: a^=b^(b^=a^b); 2.已知数组int[] max= ...
- .Net Core 之 图形验证码 本文介绍.Net Core下用第三方ZKWeb.System.Drawing实现验证码功能。
本文介绍.Net Core下用第三方ZKWeb.System.Drawing实现验证码功能. 通过测试的系统: Windows 8.1 64bit Ubuntu Server 16.04 LTS 64 ...
- java 处理XML(dom4j-1.6.1)
java 处理XML(dom4j-1.6.1) Java 处理xml有很多框架,今天使用主流框架dom4j-1.6.1 下载地址:http://www.dom4j.org/dom4j-1.6.1/ D ...
- .Net Core 之 图形验证码
本文介绍.Net Core下用第三方ZKWeb.System.Drawing实现验证码功能. 通过测试的系统: Windows 8.1 64bit Ubuntu Server 16.04 LTS 64 ...
- Dynamic CRM 2013学习笔记(三)快速创建实体 EntityCreater
一.实体简介 实体用于在 Microsoft Dynamics CRM 中建立业务数据模型和管理业务数据.例如,可以使用客户.市场活动和事件(案例)等实体跟踪和支持销售.市场营销和服务活动.实体具有一 ...
- javaweb学习总结(二十六)——jsp简单标签标签库开发(二)
一.JspFragment类介绍 javax.servlet.jsp.tagext.JspFragment类是在JSP2.0中定义的,它的实例对象代表JSP页面中的一段符合JSP语法规范的JSP片段, ...
- Hibernate常见错误整理
Hibernate常见错误合集 1.错误:object references an unsaved transient instance - save the transient instance ...
- MVC中验证码的生成
在项目中验证码的生成通常是需要页面无刷新的,所以验证码图片实际是跟在某个input后面的img,通过控制该img来控制验证码显示的位置,例如: <div> <input id=&qu ...
随机推荐
- 火狐 SSL 收到了一个弱临时 Diffie-Hellman 密钥的解决办法
连接 https网址 时发生错误. 在服务器密钥交换握手信息中 SSL 收到了一个弱临时 Diffie-Hellman 密钥. (错误码: ssl_error_weak_server_ephemera ...
- 【转帖】自助式BI的崛起:三张图看清商业智能和大数据分析市场趋势
自助式BI的崛起:三张图看清商业智能和大数据分析市场趋势 大数据时代,商业智能和数据分析软件市场正在经历一场巨变,那些强调易用性的,人人都能使用的分析软件正在取代传统复杂的商业智能和分析软件成为市场的 ...
- HTTP2 学习
一.HTTP1.x存在的问题 Http1.0时Connection无法复用,同一时间一个Connection只能处理一个request.Http1.1引入了Request pipelining来解决这 ...
- 查看JAVA进程中哪个线程CPU消耗最高
一,在centos linux 上查看进程占用cpu过高 top shift+h 查看哪个进程程消耗最高 二,查看JAVA进程中哪个线程消耗最高 2.1 导出java运行的线程信息 ...
- Windows2008 R2下,DCOM配置里的属性灰色不可用的解决方法
错误为:为应用程序池“XXXXXX”提供服务的进程在与“Windows Process Activation Service”通信时出现严重错误.该进程 ID 为"XXX".数据字 ...
- CISA 信息系统审计知识点 [第二章. IT治理和管理 ]
第二章. IT治理和管理 1. IT治理.管理.安全和控制框架及标准.指南和实践 IT治理是董事会和执行管理层的职责. IT治理的关键因素:保持与业务的战略一致,引导业务价值的实现. IT治理关注 ...
- Spark源码系列(八)Spark Streaming实例分析
这一章要讲Spark Streaming,讲之前首先回顾下它的用法,具体用法请参照<Spark Streaming编程指南>. Example代码分析 val ssc = )); // 获 ...
- 解决Spring+Quartz无法自动注入bean问题
问题 我们有时需要执行一些定时任务(如数据批处理),比较常用的技术框架有Spring + Quartz中.无奈此方式有个问题:Spring Bean无法自动注入. 环境:Spring3.2.2 + Q ...
- iOS 企业证书发布app 流程
企业发布app的 过程比app store 发布的简单多了,没那么多的要求,哈 但是整个工程的要求还是一样,比如各种像素的icon啊 命名规范啊等等. 下面是具体的流程 1.修改你的 bundle i ...
- IIS+PHP+MYSQL安装配置
首先下载php-5.2.0-win32.zip,mysql-noinstall-5.0.22-win32.zip和phpMyAdmin-2.9.1.1-all-languages.zip.这三个文件的 ...