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);
}
}
}
运行结果
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());
}
输出结果
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。