[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 ...
随机推荐
- Ubunbu新建的用户使用SecureCrt无法Table补全、无法高亮
Check 两个地方: 1. 确保/etc/passwd中配置有/bin/bash (这个是用来控制补全). 2. 在~/.bashrc中配置, export TERM=linux (这个是用来控制 ...
- [知识库分享系列] 三、Web(高性能Web站点建设)
知识库分享系列: [知识库分享系列] 二..NET(ASP.NET) [知识库分享系列] 一.开篇 分享介绍此知识库之所以为 Web 系列,因为和 .NET 没有完全的关系,其中的技术和实践实用于各种 ...
- BSS Audio® Introduces Full-Bandwidth Acoustic Echo Cancellation Algorithm for Soundweb London Conferencing Processors
BSS Audio® Introduces Full-Bandwidth Acoustic Echo Cancellation Algorithm for Soundweb London Confer ...
- .net微信公众号开发——模板消息
作者:王先荣 本文介绍微信公众号中的模板消息,包括以下内容:(1)TemplateMessage类简介:(2)设置所属行业:(3)获得模板id:(4)发送模板消息:(5)接收推送模板消息发送结果 ...
- [JS Compose] 0. Understand 'Box' or 'Container', they are just like Array!
We'll examine how to unnest function calls, capture assignment, and create a linear data flow with a ...
- juniper 550M訪问自身公网IP回流内部IP
拓扑图示意: 网关设备juniper 550M, untrust 区: 公网地址段22.22.22.22/29 trust区: 内部员工PC地址:172.16.4.x /24 trust区: ...
- 解决oracle 端口 1521 本机127可通 其他ip不通
http://wenku.baidu.com/link?url=8tRGGObqgLd6-yqprioIZSyluu9K0BgA29Lhx7F57pVDIHbMHVDNTa_SlEmVugGT4QJO ...
- Solution to “VirtualBox can't operate in VMX root mode” error in Windows 7
I was trying out various virtualization solutions on Windows 7, including Microsoft Virtual PC and V ...
- UNIX环境高级编程笔记之线程
本章涉及到线程的一些基本知识点,讨论了现有的创建线程和销毁线程的POSIX.1原语,此外,重点介绍了线程同步问题,讨论了三种基本的同步机制:互斥量.读写锁.条件变量.
- seajs集成jquery的一个坑
var $ = require("jquery"); 今天在用seajs集成js的时候,老是发现$获取不到,但是文件又加载进去了,后来找了半天发现是这个问题. 本质的原因在于sea ...