[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 ...
随机推荐
- 文件系统管理 之 Linux 创建文件系统及挂载文件系统流程详解
阅读此文,必须具备知识点:<Linux 查看磁盘分区.文件系统.使用情况的命令和相关工具介绍><实例解说 fdisk 使用方法><合理规划您的硬盘分区><Fe ...
- WPF WebBrowser屏蔽弹出alert ,confirm ,prompt ,showModalDialog() ,window.open()
WPF WebBrowser屏蔽弹出alert ,confirm ,prompt ,showModalDialog() ,window.open()添加Microsoft.mshtml.dll,然后写 ...
- 从Tmux 转到GNU Screen
网上很多地方都说Tmux比GNU Screen要好用,不过无意间看到这篇Switching from tmux to GNU Screen之后,我发现GNU Screen的窗口/区域概念更好,至少是更 ...
- centos中 mysql 5.7安装
以免授权模式启动 编辑 /etc/my.cnf,添加以下内容: linux环境中:vi /etc/my.cnf 在[MySQL(和PHP搭配之最佳组合)d]配置段添加如下两行: user=mysql ...
- 如何安装最新版本的memcached
转载自孟叔的博客: https://learndevops.cn/index.php/2016/06/10/how-to-install-the-latest-version-of-memcache ...
- Java 10大精华文章收集001
Java语言与JVM中的Lambda表达式全解 Lambda表达式是自Java SE 5引入泛型以来最重大的Java语言新特性,本文是2012年度最后一期Java Magazine中的一篇文章,它介绍 ...
- DS1337 时钟芯片在 C8051F 上的实现
一.DS1337介绍 DS1337串行实时时钟芯片是一种低功耗.全部采用BCD码的时钟日历芯片,它带有两个可编程的定时闹钟和一个可编程的方波输出.其地址和数据可通过I2C总线串行传输,能提供秒.分.时 ...
- 关于app.config不能即时保存读取的解决方案
public void saveValue(string Name, string Value) { ConfigurationManager.AppSettings.Set(Name, Value) ...
- C++:不同类型的指针的本质与差异
转自:http://blog.csdn.net/richerg85/article/details/10076365 指针的类型(The Type of a Pointer) 一 ...
- Android代码优化工具——Android lint
作为移动应用开发者,我们总希望发布的apk文件越小越好,不希望资源文件没有用到的图片资源也被打包进apk,不希望应用中使用了高于minSdk的api,也不希望AndroidManifest文件存在异常 ...