特性
特性(Attrubute)
特性是用于在运行时传递程序的元素(类,方法,结构,枚举,组件等)的行为信息的声明性标签。通过使用方括号 ( [ ] ) 来添加声明性信息。
当使用特性后,其他人可以通过反射来读取这个标签,根据标签内容来做出反应。
接下来介绍.Net框架提供的两种类型的特性:预定义特性和自定义特性。
预定义特性
预定义特性分为三种:
AttributeUsage
预定义特性AttributeUsage规定如何使用自定义的特性,就像给特性加的特性。
规定语法如下:
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]
参数列表:
- ValidOn(必选):指定可放置的语法元素(如类,方法,参数),它是枚举器AttributeTargets的值的组合,通过 | 来连接。默认为AttributeTargets.All。
- AllowMultiple(可选):是否允许在同一元素上使用多次,布尔值,默认为false(单用)。
- Inherited(可选):是否允许被派生类继承,布尔值,默认为false(不继承)。
使用示例:
using System;
// 1. 限定只能给方法贴标签
// 2. 允许一个方法贴多个该标签 (AllowMultiple = true)
// 3. 子类重写该方法时,不自动继承这个权限要求 (Inherited = false)
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class RequirePermissionAttribute : Attribute
{
public string PermissionName { get; }
public RequirePermissionAttribute(string permissionName)
{
PermissionName = permissionName;
}
}
// --- 使用场景 ---
public class AdminPanel
{
[RequirePermission("UserRead")]
[RequirePermission("UserDelete")]
public void DeleteUser()
{
// 业务逻辑
}
}
Conditional
Conditional特性的作用是条件编译控制。它能够根据是否定义了特定的预处理符号(如 DEBUG),来决定编译器是否应该包含对该方法的“调用”。
using System.Diagnostics;
public class Tool
{
[Conditional("DEBUG")] // 只有定义了 DEBUG 符号,调用处才会被编译
public static void ShowDebugInfo(string message)
{
Console.WriteLine($"DEBUG INFO: {message}");
}
}
// 调用处
public void DoWork()
{
// 在 Release 模式下,编译器会自动忽略下面这一行,就像它从未写过一样
Tool.ShowDebugInfo("正在执行核心逻辑...");
Console.WriteLine("工作完成");
}
对于Conditional特性执行时的逻辑:如果Method()附加了该特性,那么编译器会扫描所有代码,发现有调用 Method() 的地方,如果符号未定义,就把那行调用代码删掉。对于以下情况要注意:
- 反射调用: 如你用
MethodInfo.Invoke去执行,[Conditional]是拦不住的,因为它是在编译期剔除静态调用的,拦截不了运行时的反射。 - 委托(Delegate): 如果把该方法赋值给一个
Action变量再执行,特性也会失效。 - Main() : 是由 .NET 运行时(CLR) 直接启动的,而不是由代码在某处手动调用的。
Obsolete
这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。
例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。
[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]
- 参数 message:是一个字符串,描述项目为什么过时以及该替代使用什么。
- 参数 iserror:是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。
创建自定义特性
.Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。
创建并使用自定义特性包含四个步骤:
- 声明自定义特性
- 构建自定义特性
- 在目标程序元素上应用自定义特性
- 通过反射访问特性
声明自定义特性
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
构建自定义特性
在这里我们声明一个DebugInfo的自定义特性。
// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
应用自定义特性
通过放置在它的目标之前来应用该特性:
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
// 成员变量
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012",
Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
下一篇文章,将浅谈反射来使用这个信息。
Comments NOTHING