为啥修改集合也会修改 foreach 中的前一个集合

Posted

技术标签:

【中文标题】为啥修改集合也会修改 foreach 中的前一个集合【英文标题】:Why modifying a collection also modifies the previous one in a foreach为什么修改集合也会修改 foreach 中的前一个集合 【发布时间】:2022-01-01 19:00:46 【问题描述】:

基本上我的代码的目的如下: 我有一组代理,包括“矩阵”和“临时基础”。有些特工只有一个“临时基地”,他们只会在收藏中出现一次,但有些特工拥有多个“临时基地”,因此会出现多次。 如果一个代理只有一个“Temps Base”(由一个 double 表示),没问题。 如果他有多个,例如三个,我只想将它们相加,只保留一个对象,但其中包含三个双精度数的总和。

这是我的课:

public class AgentJournalier

    public string Matricule  get; set; 
    public double TempsBase get; set; 

我的收藏:

    private ObservableCollection<AgentModel> _TousAgents;
    public ObservableCollection<AgentModel> TousAgents
    
        get
        
            return _TousAgents;
        
        set
        
            if (value != _TousAgents)
            
                _TousAgents = value;
                RaisePropertyChanged(nameof(TousAgents));
            
        
    

    private ObservableCollection<AgentModel> _AgentsContratEnCours;
    public ObservableCollection<AgentModel> AgentsContratEnCours
    
        get
        
            return _AgentsContratEnCours;
        
        set
        
            if (value != _AgentsContratEnCours)
            
                _AgentsContratEnCours = value;
                RaisePropertyChanged(nameof(AgentsContratEnCours));
            
        
    

对于这个测试,我的 Collection TousAgents 将包含 3 行:

Matricule : Bec  -  TempsBase : 100
Matricule : Bec  -  TempsBase : -25
Matricule : Bec  -  TempsBase : -10

我的代码:

        foreach (AgentModel tsag in TousAgents)
        
            if (AgentsContratEnCours.Any(p => p.Matricule == tsag.Matricule) == false)
            
               AgentsContratEnCours.Add(tsag);
            
            else
            
               AgentModel AgentDejaDansLaListe = new AgentModel();
               AgentDejaDansLaListe = AgentsContratEnCours.Where(x => x.Matricule == tsag.Matricule).FirstOrDefault();

               AgentsContratEnCours.Remove(AgentsContratEnCours.Where(x => x.Matricule == tsag.Matricule).FirstOrDefault());

               AgentDejaDansLaListe.TempsBase = AgentDejaDansLaListe.TempsBase + tsag.TempsBase;

               AgentsContratEnCours.Add(AgentDejaDansLaListe);                
            
        

        foreach (AgentModel tsag in TousAgents)
        
            Console.WriteLine($"Temps base : tsag.TempsBase");
        

        foreach (AgentModel tsag in AgentsContratEnCours)
        
            Console.WriteLine($"Temps base total : tsag.TempsBase");
        

考虑到在列表中出现 3 次的代理具有相同的“矩阵”但具有 3 个不同的“TempsBase”(例如:100、-10、-25),我想要的输出如下:

Output :
Temps base : 100
Temps base : -25
Temps base : -10
Temps base total : 65

但我实际得到的输出是这个:

Output :
Temps base : 65 <== Why ??
Temps base : -25
Temps base : -10
Temps base total : 65

显然,如果我再次调用该方法,“Temps Base”会继续递减(因为它不再具有原来的值 100,而是新的值:65):

Output :
Temps base : 30 <== Why ??
Temps base : -25
Temps base : -10
Temps base total : 30

我想了解为什么“TousAgents”中的第一个值会发生变化。 我的目标是第一个集合中的值永远不会改变,所以我创建了第二个:AgentsContratEnCours。 但是,当我更改 AgentsContratEnCours 中的值时,即使我没有手动将任何内容分配给“TousAgents”,它也会在 TousAgents 中更改它。你能解释一下为什么吗?我该如何避免这种情况?

【问题讨论】:

【参考方案1】:

您对引用类型所做的有问题的假设导致您犯了这个错误。错误假设在下面的代码中,你能发现吗?

AgentModel AgentDejaDansLaListe = new AgentModel();
AgentDejaDansLaListe = AgentsContratEnCours.Where(x => x.Matricule == tsag.Matricule).FirstOrDefault();

AgentDejaDansLaListe.TempsBase = AgentDejaDansLaListe.TempsBase + tsag.TempsBase;

让我解释一下。 AgentModel 是一种引用类型,这意味着如果您在 AgentModel 中更改其属性(Marticule 和 TempsBase),它们的所有副本都会更改。

在第一行中,您创建一个名为 AgentDejaDansLaListe 的新 AgentModel,然后在第二行中将其丢弃(进入垃圾收集器),然后为其分配一个完全不同的引用。

在最后一行中,您修改了该对象的 TempsBase,这也更改了列表中该原始对象的 TempsBase,因为AgentModel 是一个引用类型。


要解决此问题,请使用 AgentModel 的结构或在其中实现 Clone。

【讨论】:

以上是关于为啥修改集合也会修改 foreach 中的前一个集合的主要内容,如果未能解决你的问题,请参考以下文章

修改 Spark RDD foreach 中的集合

集合已修改;可能无法执行枚举操作。

Swift可变集:找到重复元素

在 C# 中,为啥我不能在 foreach 循环中修改值类型实例的成员?

为啥 List<T>.ForEach 允许修改其列表?

为啥即使我修改了 lock 变量,我也会得到一个无限的 while 循环? [复制]