1
attribute
  • 可应用于类型和成员
  • 是类的一个实例
  • 从System.Attribute派生
C# attribute使用范围
  • 程序集
  • 模块
  • 类型(类、结构、枚举、接口、委托)
  • 字段
  • 方法(含构造器)
  • 方法参数
  • 方法返回值
  • 属性
  • 事件
  • 泛型类型参数
FCL定义的Attribute列举
  • Serializable:应用于类型,告诉序列化格式化器,一个实例的字段可以被序列化和反序列化
  • Flags:应用于枚举类型,将枚举类型作为一个位标志(bit flag)集合使用
默认自定义的attribute类能用于任何目标元素
public class MyAttribute : Attribute
{
    public MyAttribute()
    {

    }
}

[MyAttribute]
class Program
{
    [MyAttribute]
    static void Main(string[] args)
    {
    }
}

限定attribute的使用范围:枚举类型

[AttributeUsage(AttributeTargets.Enum, AllowMultiple = false, Inherited = false)]
public class MyEnumAttribute : Attribute
{
    public MyEnumAttribute()
    {

    }
}

以上代码利用AttributeUsage告知编译器定制attribute的合法应用范围,如果将定制attribute应用于一个无效目标时将会报错

[MyEnumAttribute] //正确
public enum MyEnum { }

[MyEnumAttribute] //错误:只对枚举有效
class Program
{
}
AllowMultiple和Inherited
  • AllowMultiple:不显示设置为true就只能向一个选定目标应用一次
  • Inherited:指明在attribute应用于基类的时候是否同时应用于派生类和重写方法上
[AttributeUsage(AttributeTargets.Enum, AllowMultiple = false, Inherited = false)]
public class MyEnumAttribute : Attribute
{
    public MyEnumAttribute()
    {

    }
}

//错误:特性重复
[MyEnumAttribute][MyEnumAttribute]
public enum MyEnum { }

/*------------------------------*/

[AttributeUsage(AttributeTargets.Enum, AllowMultiple = true, Inherited = false)]
public class MyEnumAttribute : Attribute
{
    public MyEnumAttribute()
    {

    }
}

//正确
[MyEnumAttribute][MyEnumAttribute]
public enum MyEnum { }

定制attribute时可以使用构造器获取参数,在使用过程中,必须传递一个编译时常量表达式。传递参数规则:

  • Type类型参数:必须使用C#的typeof操作符传递
  • Object参数:可传递int、string或其它任意常量表达式(包括null),如果常量表达式为值类型,那么会在运行时构造attribute实例的时候对其装箱

示例代码:

/// <summary>
/// 自定义Attribute
/// </summary>
/// <param name="name">引用string</param>
/// <param name="o">任意类型,有必要就进行装箱</param>
/// <param name="t">Type类型</param>
public MyAttribute(string name, Object o, Type t)
{

}

class Program
{
    [MyAttribute("name", 12, typeof(string))]
    static void Main(string[] args)
    {
    }
}
利用反射检查类型attribute改变代码的行为
[AttributeUsage(AttributeTargets.Enum, AllowMultiple = false, Inherited = false)]
public class MyEnumAttribute : Attribute
{
    public MyEnumAttribute()
    {

    }
}

[MyEnumAttribute]
public enum MyEnum { }

public void Test(Type enumType)
{
    if(enumType.IsDefined(typeof(MyEnumAttribute), false)){
        //如果含有MyEnumAttribute特性执行以下代码
    }
    else
    {
        //否则执行以下代码
    }
}
使用反射检查方法attribute特性
public class MyAttribute : Attribute
{
    public MyAttribute(string name, Object o, Type t) {}
}

[DebuggerDisplayAttribute("doubleJ", Name = "Name", Target = typeof(Program))]
public class Program
{
    [Conditional("Debug")]
    public void DoSomething() { }

    [MyAttribute("name", 12, typeof(string))]
    public static void Main(string[] args)
    {
        ShowAttribute(typeof(Program));

        var members = typeof(Program).FindMembers(
            MemberTypes.Method | MemberTypes.Constructor,
            BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static,
            Type.FilterName, "*"
        );

        foreach (var member in members)
            ShowAttribute(member);

        Console.ReadKey();
    }

    private static void ShowAttribute(MemberInfo memberInfo)
    {
        var attributes = Attribute.GetCustomAttributes(memberInfo);
        foreach (var attribute in attributes)
        {
            Console.WriteLine(attribute.GetType().ToString());

            if (attribute is MyAttribute)
            {

            }

            if (attribute is ConditionalAttribute)
            {

            }

            DebuggerDisplayAttribute dda = attribute as DebuggerDisplayAttribute;
            if (dda != null)
                Console.WriteLine("value = {0}, name = {1}, target = {2}", dda.Value, dda.Name, dda.Target);
        }
    }
}

运行结果
image.png

IsDefined、GetCustomAttributes、GetCustomAttribute对比
  • IsDefined:有一个指定的Attribute派生类的实例与目标关联就返回true,由于不构造任何Attribute类的实例所以效率很高
  • GetCustomAttributes:返回一个数组,每个元素都是应用于目标的指定Attribute类的一个实例。如果不为该方法指定具体的Attribute,数组中已经应用的所有Attribute的实例,如果没有应用任何Attribute类的实例返回空数组
  • GetCustomAttribute:返回应用于目标的指定Attribute类的一个实例。如果没有则返回null。如果应用了多个Attribute实例,则抛出异常

*使用IsDefined不会调用Attribute的构造器,也不会设置它的字段和属性,所以IsDefined效率最高,如果想要知道一个Attribute是否应用于一个目标,那么应该选择使用该方法

条件Attribute

使用条件Attribute后它的执行便会依赖于指定的预处理标识符,如:

[Conditional("DEBUG")]
[Conditional("TEST")]
public class CondAttribute : Attribute
{

}

*当编译器发现目标元素使用了Conditional的一个实例,那么在含有目标元素的代码在进行编译时(以上述代码为例),只有在定义了TEST或DEBUG符号的前提下,编译器才会在元数据中生成attribute信息,尽管如此,但attribute类的定义元数据和实现仍然存在于程序集中。

重写Match和Equals实现两个attribute实例的匹配
[Flags]
public enum Role
{
    Read = 0x0001,
    Write = 0x0002
}

sealed class RoleAttribute : Attribute
{
    private Role m_Role;

    public RoleAttribute(Role role)
    {
        this.m_Role = role;
    }

    public override bool Match(object obj)
    {
        if (obj == null) return false;

        if (this.GetType() != obj.GetType()) return false;

        RoleAttribute other = (RoleAttribute)obj;
        if ((other.m_Role & this.m_Role) != this.m_Role)
            return false;

        return true;
    }

    public override bool Equals(object obj)
    {
        if (obj == null) return false;

        if (this.GetType() != obj.GetType()) return false;

        RoleAttribute other = (RoleAttribute)obj;
        if (other.m_Role != this.m_Role)
            return false;

        return true;
    }

    public override int GetHashCode()
    {
        return (Int32)this.m_Role;
    }
}

[RoleAttribute(Role.Read)]
class Role1 { }

[RoleAttribute(Role.Write)]
class Role2 { }

public class Program
{
    public static void Main(string[] args)
    {
        CanRead(new Role1());
        CanRead(new Role2());
        Console.ReadKey();
    }
}

private static void CanRead(object obj)
{
    Attribute read = new RoleAttribute(Role.Read);
    Attribute validRole = Attribute.GetCustomAttribute(obj.GetType(), typeof(RoleAttribute), false);
    if((validRole!=null)&&read.Match(validRole))
        Console.WriteLine("{0} can read", obj.GetType());
    else
        Console.WriteLine("{0} can not read", obj.GetType());
}

输出结果
image.png


DoubleJ
7 声望3 粉丝

« 上一篇
委托
下一篇 »
可空值类型