能否通过C#自定义特性实现属性读写时的加解密?
嘿,我来帮你搞定这个问题!首先得明确一个关键点:单纯的自定义Attribute本身是没法直接修改属性的get/set逻辑的——它只是附着在属性上的元数据,不会自动触发任何行为。不过别担心,我们有好几种办法结合特性来实现你要的加解密需求,下面给你详细说说:
核心结论:特性本身不直接修改行为,需搭配其他机制
自定义特性的本质是元数据标记,它只会告诉程序“这个属性需要特殊处理”,但不会自动帮你处理。你需要额外的逻辑来读取这些标记,然后在属性访问时注入加解密操作。
方案1:手动在属性get/set中结合特性标记
这是最直接的方式,在属性的读写逻辑里手动调用加解密方法,特性可以用来标记哪些属性需要处理,甚至存储加解密的配置(比如密钥)。
示例代码:
[AttributeUsage(AttributeTargets.Property)] public class EncryptedAttribute : Attribute { // 可以在这里定义加解密的配置,比如密钥名称 public string KeyName { get; set; } = "DefaultKey"; } // 加解密工具类(模拟实际逻辑) public static class EncryptionHelper { public static string Encrypt(string plainText, string key) { // 替换成你的真实加密逻辑,比如AES/RSA return Convert.ToBase64String(Encoding.UTF8.GetBytes(plainText)); } public static string Decrypt(string cipherText, string key) { // 替换成你的真实解密逻辑 return Encoding.UTF8.GetString(Convert.FromBase64String(cipherText)); } } public class TargetClass { // 私有字段存储加密后的值 private string _encryptedName; [Encrypted(KeyName = "UserKey")] public string Name { get { // 读取特性配置并解密 var attr = typeof(TargetClass).GetProperty(nameof(Name)) .GetCustomAttribute<EncryptedAttribute>(); return attr == null ? _encryptedName : EncryptionHelper.Decrypt(_encryptedName, attr.KeyName); } set { // 读取特性配置并加密后存储 var attr = typeof(TargetClass).GetProperty(nameof(Name)) .GetCustomAttribute<EncryptedAttribute>(); _encryptedName = attr == null ? value : EncryptionHelper.Encrypt(value, attr.KeyName); } } }
方案2:用AOP框架自动拦截(推荐)
如果不想在每个属性里重复写加解密代码,可以用AOP(面向切面编程)框架,通过特性标记自动拦截属性的读写操作,注入加解密逻辑。
以PostSharp为例(需要安装PostSharp NuGet包):
[Serializable] [AttributeUsage(AttributeTargets.Property)] public class EncryptedAttribute : LocationInterceptionAspect { // 拦截属性读取 public override void OnGetValue(LocationInterceptionArgs args) { args.ProceedGetValue(); // 获取原始加密值 if (args.Value is string cipherText) { args.Value = EncryptionHelper.Decrypt(cipherText, "DefaultKey"); // 解密后返回 } } // 拦截属性赋值 public override void OnSetValue(LocationInterceptionArgs args) { if (args.Value is string plainText) { args.Value = EncryptionHelper.Encrypt(plainText, "DefaultKey"); // 加密后再赋值 } args.ProceedSetValue(); } } public class TargetClass { [Encrypted] public string Name { get; set; } }
这样你访问TargetClass.Name时,PostSharp会自动帮你处理加解密,完全不用手动写重复逻辑。
方案3:用.NET原生动态代理(无第三方依赖)
如果不想用第三方框架,可以用.NET的DispatchProxy创建动态代理类,读取特性标记并处理加解密:
public class EncryptedProxy<T> : DispatchProxy { private T _targetInstance; protected override object Invoke(MethodInfo targetMethod, object[] args) { // 判断是不是属性的get/set方法 if (targetMethod.IsSpecialName && (targetMethod.Name.StartsWith("get_") || targetMethod.Name.StartsWith("set_"))) { var propName = targetMethod.Name.Substring(4); var property = typeof(T).GetProperty(propName); var encryptedAttr = property.GetCustomAttribute<EncryptedAttribute>(); if (encryptedAttr != null) { if (targetMethod.Name.StartsWith("get_")) { // 读取原始加密值并解密 var encryptedValue = targetMethod.Invoke(_targetInstance, args); return EncryptionHelper.Decrypt(encryptedValue as string, encryptedAttr.KeyName); } else if (targetMethod.Name.StartsWith("set_")) { // 先加密再赋值 var plainText = args[0] as string; args[0] = EncryptionHelper.Encrypt(plainText, encryptedAttr.KeyName); } } } return targetMethod.Invoke(_targetInstance, args); } // 创建代理实例的方法 public static T Create(T target) { var proxy = Create<T, EncryptedProxy<T>>() as EncryptedProxy<T>; proxy._targetInstance = target; return proxy; } }
使用方式:
var original = new TargetClass(); var proxy = EncryptedProxy<TargetClass>.Create(original); proxy.Name = "MySecretInfo"; // 自动加密存储 Console.WriteLine(proxy.Name); // 自动解密输出"MySecretInfo"
总结
- 单纯的自定义特性只是元数据,不能直接修改属性的get/set逻辑;
- 你可以结合手动逻辑、AOP框架或动态代理来读取特性标记,注入加解密行为;
- 如果项目允许,AOP框架是最简洁的方案,能大幅减少重复代码。
内容的提问来源于stack exchange,提问作者Ray Lei
相关产品推荐
相关产品推荐

