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();
}

 

上一篇:利用raspberry pi搭建typecho笔记(三) typecho nginx sqlite FAQ


下一篇:关于同一用户不能同时登录问题的探讨(1/2)