[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 ...
随机推荐
- ubuntu 休眠之后网络间接失败 can not connect to network after suspend (wake up)
ubuntu for laptop系统在系统休眠后wakeup 之后,网络连接失败, 有线网络无法连接, 无线wifi无法连接, 只能重启后才能恢复, 此时可以采用以下方法处理: 1. 在/etc/p ...
- 【转帖】四种BI 开源工具介绍-SpagoBI,openI,JasperSoft,Pentaho
四种BI 开源工具介绍-SpagoBI,openI,JasperSoft,Pentaho 1 BI系统的简述 从技术角度来说 BI 包含了 ETL.DW.OLAP.DM等多环节.简单的说就是把交易系统 ...
- SQL的主键和外键约束
SQL的主键和外键的作用: 外键取值规则:空值或参照的主键值. (1)插入非空值时,如果主键表中没有这个值,则不能插入. (2)更新时,不能改为主键表中没有的值. (3)删除主键表记录时,你可以在建外 ...
- 优先队列求解Huffman编码 c++
优先队列小析 优先队列的模板: template <class T, class Container = vector<T>,class Compare = less< ...
- IOS开发 图形绘制,绘制线条,矩形,和垂直和居中绘制文字
概述 吐槽下IOS下 的图形绘图,代码冗长,不得不自己重新封装方法.整理形成本文. 绘制线 // 绘制直线 + (void)toDrawLineFromX:(CGFloat)x1 Y:(CGFloat ...
- 日本超人气洛比(Robi)声控机器人
1.日本超人气洛比(Robi)声控机器人. http://technews.cn/2015/04/18/interview-with-robi-creator-tomotaka-takahashi/ ...
- MINE
MINE MINE is an app for the nearly 1.2 million songwriters, composers, musicians, and publishers who ...
- Dbvisualizer9.0.6 解决中文乱码
一.设置编辑器的编码 Tools->Tools Properties ->General->File Encoding 设置为UTF-8 二.如果数据库为UTF-8,则要在连接时做以 ...
- 自己动手搭建 CAS(Central Authentication Service) 环境,为了单点登录(Single Sign On , 简称 SSO )
介绍 刚刚搭建 CAS 成功了,现在记录下来,怕以后忘记,同时也给需要帮助的人.CAS 搭建需要服务端和客户端,服务端是 Java 写的,如果自己搭建则需要安装与配置 Java 环境.客户端可以是多种 ...
- Git使用日记
git是个分布式的版本管理工具,现在我们前端这边用它做版本管理.之前也看过一些相关资料,不过没有使用它管理过项目代码.如今,用它也有段时间了所以就写些东西,仅供参考. ###快速上手 工作经常用到的几 ...