You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:09:33