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

为何PropertyInfo.SetValue会修改源字典值?如何规避?

问题:修改对象属性时意外修改了静态字典中的缓存值

你遇到的情况是:用静态字典做缓存,把字典里的Country对象赋值给Person的Country属性后,调用encodeObj方法修改这个对象的字符串属性时,发现字典里的原缓存值也被改了,就算复制字符串也没用。

你的代码示例:

Person p = new Person(); 
//Cache.Countries是存储国家信息的静态字典
//每个Country包含多个属性,不止description
p.Country = Cache.Countries[1]; 
encodeObj(p.Country);

private void encodeObj(object obj) { 
    foreach(var pi in obj.GetType().GetProperties()) { 
        object propValue = pi.GetValue(obj,null); 
        if(pi.PropertyType == typeof(string) && !string.IsNullOrEmpty((string)propValue)) { 
            //尝试复制字符串但无效
            string temp = string.Copy((string)propValue); 
            string encoded = WebUtility.HtmlEncode(temp); 
            //执行前Cache.Countries[1]对应属性值正常
            pi.SetValue(obj,encoded); 
            //执行后Cache.Countries[1]对应属性值已变为编码后的值!
        } 
    } 
}

解决方案:断开对象引用,使用独立副本

嘿,这个问题我太熟了——你踩的是引用类型传递的经典坑!

为什么复制字符串没用?

因为你复制的只是字符串本身,但问题根本不在字符串上:p.Country = Cache.Countries[1]这行代码并没有创建新的Country对象,只是把字典里那个对象的内存引用赋值给了p.Country。也就是说,p.Country和Cache.Countries[1]指向的是同一个内存里的对象,你修改p.Country的任何属性,本质上都是在修改字典里的那个原对象,复制字符串当然没用啦。

怎么解决?

核心思路就是:给p.Country一个独立的Country对象副本,让它和字典里的缓存对象彻底断开引用。这里有几种常用方法:

1. 给Country类手动实现克隆方法

如果Country的属性不多,手动写一个Clone方法最直接:

public class Country
{
    // 假设你的Country类有这些属性
    public string Description { get; set; }
    public string Code { get; set; }
    // 其他属性...

    // 实现克隆方法,复制所有属性到新对象
    public Country Clone()
    {
        return new Country
        {
            Description = this.Description,
            Code = this.Code,
            // 把所有属性都复制一遍
        };
    }
}

// 赋值的时候用克隆后的对象
p.Country = Cache.Countries[1].Clone();

2. 用序列化做通用深拷贝

如果Country属性很多,或者你不想手动写克隆逻辑,可以用序列化的方式做深拷贝(比如用Newtonsoft.Json):

using Newtonsoft.Json;

// 写一个通用的深拷贝工具方法
public static T DeepCopy<T>(T obj)
{
    if (obj == null) return default;
    string jsonStr = JsonConvert.SerializeObject(obj);
    return JsonConvert.DeserializeObject<T>(jsonStr);
}

// 使用的时候
p.Country = DeepCopy<Country>(Cache.Countries[1]);

这种方法会把对象序列化成JSON字符串,再反序列化成新对象,相当于创建了一个完全独立的副本。

3. 如果是值类型(struct)直接赋值就行

不过看你的代码,Country应该是类(引用类型),如果是struct的话,直接赋值就是值复制,不会有这个问题,但业务场景里国家信息一般都是类,所以这个方法可能不适用。

总结

只要让p.Country指向一个和字典缓存无关的新对象,再去修改它的属性,就不会影响到原缓存里的值了。

内容的提问来源于stack exchange,提问作者Batsheva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:29:12