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

循环体中引用类型值的使用问题及优化设计模式咨询

问题根源分析

你的代码核心问题是**StdOperation是有状态的实现类**:每次调用IsMatch都会更新内部的_nameAry引用,所有匹配的Std对象的NameAry都指向同一个List实例,最终所有匹配项的结果都会被最后一次调用覆盖。

改进方案

方案1:改为无状态接口设计(推荐)

让操作直接返回处理结果,去掉接口的状态属性,从根源避免共享引用问题。

调整接口与实现

public interface IStudentOperation
{
    // 返回null表示不匹配,否则返回拆分后的独立列表
    List<string> SplitNameIfMatch(string name);
}

public class StdOperation : IStudentOperation
{
    public List<string> SplitNameIfMatch(string name)
    {
        if (name.IndexOf("/") == -1)
        {
            return null;
        }
        // 每次调用都生成新列表,避免引用共享
        return name.Split("/").ToList();
    }
}

修改Factory逻辑

public class Factory
{
    private readonly IStudentOperation stdOpt;

    public Factory(IStudentOperation stdOpt)
    {
        this.stdOpt = stdOpt;
    }

    public void Do(List<Std> stdAry)
    {
        foreach (var item in stdAry)
        {
            var splitResult = this.stdOpt.SplitNameIfMatch(item.Name);
            if (splitResult != null)
            {
                item.NameAry = splitResult;
            }
        }
    }
}

方案2:用值对象封装匹配结果

如果需要同时返回匹配状态和结果,可以封装成值对象,语义更清晰:

定义结果对象与接口

public class NameSplitResult
{
    public bool IsMatch { get; }
    public List<string> NameAry { get; }

    private NameSplitResult(bool isMatch, List<string> nameAry)
    {
        IsMatch = isMatch;
        NameAry = nameAry;
    }

    public static NameSplitResult Match(List<string> nameAry)
    {
        return new NameSplitResult(true, nameAry);
    }

    public static NameSplitResult NoMatch()
    {
        return new NameSplitResult(false, null);
    }
}

public interface IStudentOperation
{
    NameSplitResult ProcessName(string name);
}

public class StdOperation : IStudentOperation
{
    public NameSplitResult ProcessName(string name)
    {
        if (name.IndexOf("/") == -1)
        {
            return NameSplitResult.NoMatch();
        }
        return NameSplitResult.Match(name.Split("/").ToList());
    }
}

调整Factory逻辑

public void Do(List<Std> stdAry)
{
    foreach (var item in stdAry)
    {
        var result = this.stdOpt.ProcessName(item.Name);
        if (result.IsMatch)
        {
            item.NameAry = result.NameAry;
        }
    }
}

方案3:临时修复(不推荐)

如果无法修改原有接口,可以在赋值时创建列表副本,强行切断引用:

// 在Factory的Do方法中修改赋值逻辑
item.NameAry = new List<string>(this.stdOpt.NameAry);

这种方式只是治标不治本,有状态的服务类后续仍可能引发其他隐藏问题。

核心设计思路

服务类(如IStudentOperation的实现)应尽量设计为无状态:无状态的类更安全、易复用、易测试,从根源避免了共享可变状态带来的引用问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 14:01:04