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

ImmutableList意外被修改求助:如何确保其数据不可篡改?

解决ImmutableList中元素对象属性被意外修改的问题

问题描述

我在Class2中实现了GetItemCode方法,该方法接收条码字符串并返回StkBarcode对象,依赖名为listStkBarcodes的ImmutableList作为参考数据进行匹配。但发现Class1中修改从GetItemCode获取的StkBarcode对象的ItemCode属性后,listStkBarcodes内的对应数据也被修改,尽管代码从未直接编辑该ImmutableList。

相关代码片段:

// Class2中的ImmutableList定义
private ImmutableList<StkBarcode> listStkBarcodes;

// 调用GetItemCode的代码
StkBarcode objStkBc = await helper.GetItemCode(strBarcode, TheShippers, ShipperMfCodesList);

// ImmutableList的填充方法
public async Task<ImmutableList<StkBarcode>> GetReadOnlyStkBarcodes()
{
    ImmutableList<StkBarcode> TheList = ImmutableList.Create<StkBarcode>();
    List<StkBarcode> theList = await _context.StkBarcodes.Where(b => b.ItemCode != null
                   && b.Barcode != null).ToListAsync();
    TheList = theList.ToImmutableList();
    return TheList;
}

// StkBarcode类定义(略)

问题根源

ImmutableList仅保证集合本身的结构不可变(无法添加、删除、替换元素),但集合中存储的StkBarcode是引用类型,集合保存的是对象的内存引用。当外部获取到这个引用并修改对象属性时,实际修改的是同一块内存中的数据,因此listStkBarcodes内的原对象也会被同步修改。

可行解决方案

1. 将StkBarcode改为不可变类

把StkBarcode的所有属性设置为只读,仅通过构造函数初始化属性,从根源上禁止外部修改对象状态:

public class StkBarcode
{
    public string ItemCode { get; }
    public string Barcode { get; }
    // 其他属性同样设置为只读

    // 通过构造函数初始化所有属性
    public StkBarcode(string itemCode, string barcode /* 其他必要参数 */)
    {
        ItemCode = itemCode;
        Barcode = barcode;
        // 初始化其他属性
    }
}

如果需要修改属性值,必须创建新的StkBarcode实例,不会影响原集合中的对象。

2. 返回对象副本而非原引用

在GetItemCode方法中,找到匹配的对象后,创建一个新的StkBarcode实例返回,避免直接返回集合内的对象引用:

public async Task<StkBarcode> GetItemCode(string strBarcode, ...)
{
    var matchedItem = listStkBarcodes.FirstOrDefault(b => b.Barcode == strBarcode);
    if (matchedItem == null)
        return null;

    // 返回新的对象副本
    return new StkBarcode
    {
        ItemCode = matchedItem.ItemCode,
        Barcode = matchedItem.Barcode,
        // 复制其他所有属性
    };
}

外部修改返回的副本时,只会影响新实例,不会改动listStkBarcodes中的原对象。

3. 使用C# 9+记录类型(Record)

记录类型默认是不可变的,且支持通过with表达式创建修改后的新实例,适合这种场景:

// 定义记录类型
public record StkBarcode(string ItemCode, string Barcode /* 其他属性 */);

当需要修改属性时,必须通过with创建新实例,原对象保持不变:

// 创建修改后的新实例,原objStkBc不受影响
var modifiedItem = objStkBc with { ItemCode = "new_item_code" };

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 11:01:00