List.Add(x). x是引用还是值?

4
关于这段数据和(伪)代码:
List<myClass> Periods = new List<myClass>();

// contents of Periods is:
Periods[0] = "000";
Periods[1] = "111";
Periods[2] = "222";
Periods[3] = "REST"; // REST PERIOD
Periods[4] = "444";
Periods[5] = "REST"; // REST PERIOD
Periods[6] = "666";
Periods[7] = "777";
Periods[8] = "888";

以下代码遍历列表,并将多个列表元素作为它们在 REST 期间出现的多个列表中拉出来,将这些列表添加到“列表的列表”中。在休息期间可能会有一个或多个列表条目。
List<List<Period>> DutyDayPeriods = new List<List<Period>>();
List<Period> thisList = new List<Period>();

for (int i = 0; i < Periods.Count; i++ ) {    
    thisList.Add(Periods[i]);

    if (Periods[i].Rest == "REST") {   
        DutyDayPeriods.Add(thisList);
        thisList.Clear();   
    }    
}

在for循环底部,DutyDayPeriods<>包含1-n个相同的“thisList”副本。基本上是在最后一个休息时间和列表结束之间的最后一组列表项:
因此,我期望如下:
DutyDayPeriods[0]
    Period 000
    Period 111
    Period 222
DutyDayPeriods[1]
    Period 444
DutyDayPeriods[2]
    Period 666
    Period 777
    Period 888

但实际上我得到的结果是:
DutyDayPeriods[0]
    Period 666
    Period 777
    Period 888
DutyDayPeriods[1]
    Period 666
    Period 777
    Period 888
DutyDayPeriods[2]
    Period 666
    Period 777
    Period 888

thisList的行为像是一个引用(reference)而不是一个值(value)

每当thisList发生变化时,它似乎会对已经添加到DutyDayPeriods中的项目进行追溯更改。

这是真的吗?如果是这样,我该如何实现我想做的事情呢?

谢谢。

编辑:

我修改了以下内容,以防止休息时间自身被包括在内。 以防万一你们中有人注意到了这一点。 ;-)

if (Periods[i].Rest != "REST") {
    thisList.Add(Periods[i]);
}
else {
    DutyDayFlights.Add(thisList);
    thisList = new List<Period>();  
}

在任何C#参考资料(书籍、网页、教程等)中阅读_Value Types_和_Reference Types_是一个好主意。这种区别在C#和其他.NET语言中非常重要。在C#中,您创建的任何类型作为_class_都是引用类型,而其他任何类型(通常是_struct_类型,但也包括枚举等)都是值类型。不出所料,引用类型对象的变量通过引用引用它们。值类型变量包含值。 - Flydog57
1
关于编辑:还要注意最后一组项目的情况。 - H H
是的,我确实需要以不同的方式处理最后一个块。很好发现! - KWallace
2个回答

6

是的,List<T> 总是一个引用。

您只需要将 .Clear() 替换为创建一个新列表即可。

DutyDayPeriods.Add(thisList);
//thisList.Clear();   
thisList = new List<Period>();

那么,创建一个新列表会销毁旧列表,从而破坏对它的任何引用?这很有道理。 - KWallace
没有任何东西被销毁,甚至垃圾也没有被回收。当你的最终结果是一个列表的列表时,你需要保留它们全部来填充它。 - H H

1
短答案是可以,你正在使用引用。长答案更加复杂。C#通过值处理简单类型(例如intchar)和结构体,但类对象始终通过引用处理。因此,当您使用Period对象调用thisList.Add时,实际上是发送对该对象的引用。如果您实际上想要的是该值,则应执行以下操作:
DutyDayPeriods.Add(thisList);
thisList = new List<Period>();

这将取消引用thisList上的所有内容,意味着仅剩下的“活动”引用将在您的其他集合中。

3
不,此列表中的任何内容在此代码执行后都不符合GC的条件。 - H H
2
“但更复杂的对象总是通过引用处理。”这是具有误导性的;您可以拥有一些非常复杂的值类型 - 比许多类更复杂;有趣的问题变成了:这里是否有“对象”作为逃生条款?重点是:存在一种既不是基元也不是类的类别; - Marc Gravell
@MarcGravell 请原谅我,但我需要一个例子来理解这个问题。公平地说,我的C#经验只有4年,但我还没有看到任何不是通过引用传递的原始数据类型,虽然我想结构体可能是这个规则的例外。 - Woody1193
不,你还是没有理解重点。在这个列表中的引用_in_被添加到DutyDayPeriods中,就在它被新的引用覆盖之前。垃圾回收器在这里没有什么要做的,所有东西都是可达的。 - H H
@HenkHolterman 你确定吗?所有的对象都是这样,但是当thisList再次被newed-up时,单独的指针必须被删除。这些必须以某种方式处理(返回到内存中)。 - Woody1193
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接