CLR via C# 笔记 -- 特性(18)
1. 特性继承自System.Attribute,能作用于TypeDef(类、结构、枚举、接口和委托),MethodDef(含构造器),ParamDef,FieldDef,PropertyDef,EventDef,AssemblyDef,ModuleDef。
2. AttributeUsageAttribute 设置特性作用范围,AllowkMultiple = true 允许多次设置,Inherited = true 同时应用于派生类和重写方法。
3. 应将特性想像成逻辑状态容器。应该只提供一个公共构造器来接受特性的强制性(定位性)状态信息。
4. 当编译器检测到向目标元素应用了定制特性时,会调用特性类的构造器,向它传递任何指定的参数,从而构造特性类的实例。然后,编译器采用增强型构造器语法所指定的值,对任何公共字段和属性进行初始化。构造并初始化好定制特性类的对象后,编译器将它的状态序列化到目标元素的元数据表记录中。为方便理解,可以这样想象定制特性:它是类的实例,被序列化成驻留在元数据中的字节流。运行时可对元数据中的字节进行反序列化,从而构造出类的实例。实际发生的事情是:编译器在元数据中生成创建特性类的实例所需的信息。每个构造器参数都是1字节的类型ID,后跟具体的值。对构造器的参数进行序列化时,编译器先写入字段/属性名称,再跟上1字节的类型ID,最后是具体的值。如果是数组,则会先保存数组元素的个数,再跟上每个单独的元素。
5. Type 的 IsDefined方法要求系统查看枚举类型的元数据。
6. 通过 System.Reflection.CustomAttributeExtensions.GetCustomAttributes()方法获取特性
[Serializable] [DefaultMember("Main")] [DebuggerDisplay("Richter", Name = "Jeff", Target = typeof(Program))] public class Program { [Conditional("Debug")] [Conditional("Release")] public void DoSomeThing() { } [CLSCompliant(true)] [STAThread] public static void Main() { ShowAttributes(typeof(Program)); var members = from m in typeof(Program).GetTypeInfo().DeclaredMembers.OfType<MethodBase>() where m.IsPublic select m; foreach (var member in members) { ShowAttributes(member); } } private static void ShowAttributes(MemberInfo attributeTarget) { var attributes = attributeTarget.GetCustomAttributes<Attribute>(); Console.WriteLine("Attributes applied to {0}:{1}", attributeTarget.Name, (attributes.Count() == 0 ? "None" : string.Empty)); foreach (Attribute attribute in attributes) { 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(); } }
7. 特性实例相互匹配,用 Equip 或 Match
[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 bool Match(object obj) { 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 bool Equals(object obj) { 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; } public override int GetHashCode() { return (int)m_accounts; } } [Accounts(Accounts.Savings)] internal sealed class ChildAccount { } [Accounts(Accounts.Savings | Accounts.Checking | Accounts.Brokerage)] internal sealed class AdultAccount { } public sealed class Program { public static void Main() { CanWirteCheck(new ChildAccount()); CanWirteCheck(new ChildAccount()); CanWirteCheck(new Program()); } private static void CanWirteCheck(object obj) { Attribute checking = new AccountsAttribute(Accounts.Checking); Attribute validAccounts = Attribute.GetCustomAttribute(obj.GetType(), typeof(AccountsAttribute), false); 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()); } } }
8. 查询CostomAttributeData对象,Constructor属性指出构造器方法将如何调用。ConstructorArguments属性以一个IList<CustomAttrbuteTypedArgument>实例的形式返回将传给这个构造器的实参。NamedArguments属性以一个IList<CustomAttributeNamedArgument>实例的形式,返回将设置的字段/属性
private static void ShowAttributes(MemberInfo attributeTarget) { var attributes = CustomAttributeData.GetCustomAttributes(attributeTarget); Console.WriteLine("Attributes applied to {0}: {1}", attributeTarget.Name, (attributes.Count == 0 ? "None" : string.Empty)); foreach (var attribute in attributes) { // 显示所应用的每个特性的类型 var t = attribute.Constructor.DeclaringType; Console.WriteLine(" {0}", t.ToString()); Console.WriteLine(" Constructor called={0}", attribute.Constructor); var posArgs = attribute.ConstructorArguments; Console.WriteLine(" Positional arguments passed to constructor:{0}", (posArgs.Count == 0) ? "None" : string.Empty); foreach (var pa in posArgs) { Console.WriteLine(" Type={0}, Value={1}", pa.ArgumentType, pa.Value); } var namedArgs = attribute.NamedArguments; Console.WriteLine(" Named arguments set after construction:" + ((namedArgs.Count == 0) ? "None" : string.Empty)); foreach (var na in namedArgs) { Console.WriteLine(" Name={0},Type={1},Value={2}", na.MemberInfo.Name, na.TypedValue.ArgumentType, na.TypedValue.Value); } Console.WriteLine(); } Console.WriteLine(); }