在开始循环之前,我声明了List <T>
和List<List<T>>
。然后会将某些元素添加到List <T>
中。条件完成后,我将List <T>
添加到List<List<T>>
中。此时,我需要Clear
List <T>
,以便可以再次使用它,但是这也删除了List<List<T>>
中的元素,这是我不想发生的事情,我该怎么办?
int counter;
List<List<T>> parent= new List<List<T>>();
List<T> child= new List<T>();
for (counter = 0; counter <= group.Count - 2; counter++)
{
// some code here
if ( /* condition */ )
{
child.Add(element);
child.Add(element2);
parent.Add(child);
child.Clear();
}
}
注意:我知道解决方案之一是在循环内使用new List<T>();
,但这对我来说不是一个选择。
答案 0 :(得分:3)
您不能重复使用子列表。列表是类,因此是引用类型。这意味着您实际上并不将子列表对象本身存储在父列表中,而是引用了您创建的唯一子列表。结果,列表的所有位置都指向相同的唯一子列表。 您每次必须创建一个新的子列表。
List<List<T>> parent = new List<List<T>>();
for (counter = 0; counter <= group.Count - 2; counter++)
{
if (condition)
{
var child = new List<T>();
child.Add(element);
child.Add(element2);
parent.Add(child);
}
}
这可以像这样可视化:
child list object
variable +-----------+
+-------+ | |
|child o------------>| |
+-------+ | |
| |
| |
| |
| |
| |
+-----------+
重新使用该列表的结果将是:
child list
+-----------+
parent list +--->| |
+--------+ | |-----------|
| |o----->| | |
|--------| | |-----------+
| |o----->| | |
|--------| | +-----------+
| |o----->|
|--------| |
| |o----->|
|--------| |
| |o------+
+--------+
答案 1 :(得分:2)
由于= new List<T>()
不是您的选择,
您可以在将其添加到ToList()
时在child List
上调用parent List
。
parent.Add(child.ToList());
这将导致将一个新的单独列表添加到parent
。
然后,清除child List
是安全的。
int counter;
List<List<T>> parent= new List<List<T>>();
List<T> child= new List<T>();
for (counter = 0; counter <= group.Count - 2; counter++)
{
// some code here
if (//condition)
{
child.Add(element);
child.Add(element2);
parent.Add(child.ToList());
child.Clear();
}
}
答案 2 :(得分:0)
如果你真的想避免分配一个新的列表,那么你必须使用另一种语言。 C# 根本无法执行您要求它执行的操作。
C# 列表是 reference types。当您编写 List<T> x = y;
时,您只需将 x
设为 y
的别名(从技术上讲,您将 y
的引用存储到 x
中)。您不会将 y
引用的实际元素列表复制到 x
中,而只是对任何给定时刻 y
碰巧持有的任何内容的引用。如果您将任何内容更改为 y
,x
将仅反映这些更改。例如,如果您清除 y
,x
将引用一个空列表。
如果您想在分配 y
时保留 x
的状态,则必须创建 y
的副本并让 x
引用该副本。
然后你必须决定how deep that copy is。
为了让您的列表完全“可重用”(以便您可以对其进行所有可以想象的操作,而不会以任何方式影响 x
引用的列表),您需要一个代价高昂且很可能无用的深拷贝.
如果您只是希望能够随心所欲地添加或删除元素,那么浅拷贝就足够了。
该复制过程,无论是否浅薄,都将涉及在某个时刻创建一个新的 List 实例。这就是引用类型的工作方式。你不能把副本变魔术。因此,如果某些奇怪的规则禁止您创建 List<T>
的新实例,则您的问题没有解决方案。
现在,如果是你无能的老板欺负你采取一些不允许使用“新列表”的“良好做法”,那么扫除地毯下的污垢可能是一种选择,例如:
使用 ToList
偷偷复制列表。
List<T> PleaseMyCluelessBoss (List<T> myNotToBeAllocatedList)
{
return myNotToBeAllocatedList.ToList();
}
在整个列表上使用 GetRange
(选择所有元素并将它们复制到新列表中)
List<T> PleaseMyCluelessBoss (List<T> myNotToBeAllocatedList)
{
return myNotToBeAllocatedList.GetRange(0, myNotToBeAllocatedList.Count);
}
还有其他更复杂的方法可以做同样的事情,但是无论您使用什么复杂的解决方法,都会分配一个新列表,然后将原始列表复制到其中,然后您'最终会做与调用 parent.Add(new List<T>(child))
相同的事情,这可以说是制作列表浅拷贝的最简单方法。啊,但你的要求会得到满足,你的老板会很高兴:看不到“新名单”。
查看您的代码片段,child
列表除了立即填充、复制和清空之外别无其他用途,这基本上是 list initializer 所做的,只是以更有效的方式。如果你问我,明智的做法是取消它:
int counter;
List<List<T>> parent= new List<List<T>>();
// no need to allocate anything there
for (counter = 0; counter <= group.Count - 2; counter++)
{
// some code here
if ( /* condition */ )
{
// allocate your list on the spot and put it where it belongs
parent.Add(new List<T> { element, element2 });
}
}
或者,如果您的“某些代码”碰巧向 child
添加元素,则此版本会将自上次添加以来收集的所有内容转储到 parent
中:
int counter;
List<List<T>> parent= new List<List<T>>();
List<T> child= new List<T>();
for (counter = 0; counter <= group.Count - 2; counter++)
{
// some code possibly adding elements to child
if (/* condition */)
{
child.Add(element);
child.Add(element2);
parent.Add(child);
// don't "reuse" the list, just "reuse" the reference to start with a brand new list
child = new List<T>();
}
}
当然你有理由不这样做,但是你给出的例子让我一直在猜测为什么你首先要“重用”这个列表......