一、特性(Attributes):代码世界的“元数据魔法师”
1.1 什么是特性?
特性是C#中用于向程序元素(类、方法、字段等)附加元数据的机制,本质是继承自System.Attribute
的类。它们像“魔法斗篷”一样,为代码元素添加额外信息,这些信息可在编译期或运行时被读取和利用。
1.1.1 特性核心特性
- 元数据载体:存储与代码逻辑无关的描述性信息。
- 反射可读:通过反射(Reflection)动态获取特性信息。
- 编译器指令:如
[Obsolete]
标记废弃代码,[DllImport]
调用本地库。
二、自定义特性:从0到1打造你的“代码魔法”
2.1 自定义特性的底层实现
2.1.1 基础结构
// 自定义特性类,必须继承System.Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, // 可应用的目标
AllowMultiple = true, // 是否允许多次应用
Inherited = false)] // 是否可继承
public sealed class MyCustomAttribute : Attribute
{
// 位置参数(必须通过构造函数定义)
public string Description { get; }
// 命名参数(通过公共属性/字段定义)
public int Priority { get; set; }
// 构造函数定义位置参数
public MyCustomAttribute(string description)
{
Description = description;
}
}
2.1.2 特性应用示例
// 应用自定义特性到类
[MyCustom("这是一个魔法类", Priority = 1)]
public class MagicClass
{
// 应用特性到方法
[MyCustom("魔法方法", Priority = 2)]
public void CastSpell()
{
// ...
}
}
2.2 特性参数的“魔法契约”
2.2.1 位置参数 vs 命名参数
// 位置参数必须放在首位,且顺序不可变
[MyCustom("描述", Priority = 10)] // 合法
[MyCustom(Priority = 10, "描述")] // 报错:位置参数必须先出现
2.2.2 多特性应用
// 允许多次应用特性(需设置AllowMultiple = true)
[MyCustom("第一次"), MyCustom("第二次")]
public class MultiMagicClass
{
// 可通过反射获取所有实例
}
三、内置特性:C#的“魔法宝库”
3.1 编译期魔法:[Obsolete]
// 标记方法为废弃,并指定错误级别
[Obsolete("请使用NewMethod替代", true)] // true表示编译报错
public void OldMethod()
{
// ...
}
3.2 运行时魔法:[Conditional]
// 条件编译:仅在定义DEBUG时调用
[Conditional("DEBUG")]
public void DebugLog(string message)
{
Console.WriteLine($"DEBUG: {message}");
}
// 调用时,非DEBUG模式下代码会被忽略
public void Test()
{
DebugLog("仅在调试时显示"); // 非DEBUG模式下不执行
}
3.3 高级魔法:[DllImport]
// 调用本地DLL函数
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
四、特性驱动的“反射魔法”
4.1 通过反射获取特性信息
// 获取类上的特性
var type = typeof(MagicClass);
var attributes = type.GetCustomAttributes<MyCustomAttribute>();
foreach (var attr in attributes)
{
Console.WriteLine($"描述: {attr.Description}, 优先级: {attr.Priority}");
}
// 获取方法上的特性
var method = type.GetMethod("CastSpell");
var methodAttr = method.GetCustomAttribute<MyCustomAttribute>();
Console.WriteLine($"方法优先级: {methodAttr.Priority}");
五、实战案例:权限控制魔法系统
5.1 自定义权限特性
// 定义权限特性
[AttributeUsage(AttributeTargets.Method)]
public class PermissionAttribute : Attribute
{
public string Role { get; }
public PermissionAttribute(string role)
{
Role = role;
}
}
// 应用特性到方法
public class SecureService
{
[Permission("Admin")]
public void DeleteData()
{
// ...
}
}
5.2 反射拦截器实现
public class PermissionInterceptor
{
public void ExecuteMethod(object instance, MethodInfo method)
{
// 获取方法上的权限特性
var attr = method.GetCustomAttribute<PermissionAttribute>();
if (attr != null)
{
// 检查用户权限(模拟)
if (!IsUserInRole(attr.Role))
{
throw new UnauthorizedAccessException();
}
}
// 执行方法
method.Invoke(instance, null);
}
private bool IsUserInRole(string role)
{
// 实际实现:从用户会话获取角色
return role == "Admin";
}
}
六、特性进阶:面向切面编程(AOP)
6.1 用特性实现日志切面
// 定义日志特性
[AttributeUsage(AttributeTargets.Method)]
public class LogAttribute : Attribute
{
public string LoggerName { get; }
public LogAttribute(string loggerName)
{
LoggerName = loggerName;
}
}
// 应用特性到方法
public class Service
{
[Log("PerformanceLogger")]
public void ProcessData()
{
// ...
}
}
// 切面拦截器
public class AspectWeaver
{
public void ExecuteWithLogging(object instance, MethodInfo method)
{
var attr = method.GetCustomAttribute<LogAttribute>();
if (attr != null)
{
Console.WriteLine($"[LOG] {attr.LoggerName} 开始执行 {method.Name}");
}
// 执行方法
method.Invoke(instance, null);
if (attr != null)
{
Console.WriteLine($"[LOG] {method.Name} 执行完成");
}
}
}
七、陷阱与避坑指南
7.1 命名冲突的“魔法陷阱”
// 错误:字段名与'field'关键字冲突(C#14新特性)
public class ConflictingClass
{
private string field; // ❌ 与C#14的隐式字段冲突
// 解决方案:使用@转义或重命名
private string @field; // ✅
}
7.2 性能优化的“魔法咒语”
// 避免在高频方法中使用反射获取特性
// 优化方案:缓存特性信息
public class AttributeCache
{
private static readonly Dictionary<Type, List<Attribute>> _cache = new();
public static List<Attribute> GetCachedAttributes(Type type)
{
if (!_cache.TryGetValue(type, out var attrs))
{
attrs = type.GetCustomAttributes().ToList();
_cache[type] = attrs;
}
return attrs;
}
}
八、未来魔法:C#14的“field”关键字
// 新特性:直接引用隐式字段
public class SimpleName
{
// 简化属性定义
public string Name
{
get => field;
set => field = value.Trim(); // 自动处理空格
}
}
// 对比传统写法:无需显式声明字段
// private string _name;
// public string Name { get => _name; set => _name = value; }
九、终极案例:动态配置系统
9.1 配置特性
[AttributeUsage(AttributeTargets.Property)]
public class ConfigAttribute : Attribute
{
public string Key { get; }
public ConfigAttribute(string key)
{
Key = key;
}
}
public class AppConfig
{
[Config("timeout")]
public int Timeout { get; set; }
[Config("maxRetries")]
public int MaxRetries { get; set; }
}
9.2 动态加载配置
public class ConfigLoader
{
public void LoadConfig(AppConfig config, Dictionary<string, string> source)
{
foreach (var prop in typeof(AppConfig).GetProperties())
{
var attr = prop.GetCustomAttribute<ConfigAttribute>();
if (attr != null && source.TryGetValue(attr.Key, out var value))
{
prop.SetValue(config, Convert.ChangeType(value, prop.PropertyType));
}
}
}
}
十、 代码魔法的终极形态
通过本文的深度实践,开发者可以掌握:
- 自定义特性的“魔法契约”:从定义到反射的全流程
- 内置特性的“编译指令”:
[Obsolete]
、[Conditional]
等实战 - AOP的“切面魔法”:用特性实现日志、权限控制
- C#14的“语法糖”:
field
关键字简化属性管理