一般来说,最好是这样做:
public void Foo1(List<int> list)
{
list.Add(1);
}
或者这个:
public List<int> Foo2()
{
List<int> list = new List<int>();
list.Add(1);
return list;
}
我问的原因是因为我现在是第一种方式(除了方法不同,显然更复杂),这要求我总是使用两行来调用方法(这个加起来很多额外的一行):
List<int> list = new List<int>();
Foo1(list);
而第二种方式我可以使用一行:
List<int> list = Foo2();
那么考虑到时间和空间,哪种方式更好?
编辑:好的,更具体地说,我有一个方法可以将类型T的所有控件从ControlCollection添加到List。
public static void GetControlsRec<T>(Control.ControlCollection controlCollection, List<T> resultCollection) where T : Control
{
foreach (Control control in controlCollection)
{
if (control is T)
resultCollection.Add((T)control);
if (control.HasChildren)
GetControlsRec(control.Controls, resultCollection);
}
}
在这种情况下哪个更好?
答案 0 :(得分:5)
通常,我通常会尝试避免更改/改变现有集合。因此,我几乎总是喜欢你的第二个选择。
话虽如此,它确实有创建 new List<T>
的缺点,这意味着更多的内存分配。如果(并且仅当)性能是特定代码段中的问题,您可能需要考虑直接修改输入列表,但我建议选择一个方法名称,这样可以明显地改变集合。< / p>
答案 1 :(得分:5)
所以你遇到的问题是你有一个递归方法,其中对方法的每次调用都在概念上将一些项添加到结果集合中。这导致了两种概念方法,您可以正确识别它们:
让每个递归调用返回它所代表的所有结果的集合。这要求每次调用都会提取任何递归调用的结果,并将它们添加到自己的集合中。这是相当低效和浪费的;你最终一遍又一遍地复制数据。 (也就是说,除非你使用的数据结构可以有效地“添加来自同一类型的另一个实例的所有结果”。一个LinkedList
(你自己卷了,因为.NET版本不支持这个)可以做得好,或者也许是一些不可变的数据结构。)
传入一个可变的集合类型,让每个递归调用都会改变集合。这将表现良好,但会导致代码难以推理的问题。你不能只拿出一个“子树”并孤立地看待它。调用者也很尴尬,因为他们需要创建一个集合,将其留空,存储对它的引用,以便在调用方法后可以访问它等等。这只是非常容易混淆和容易出错。
选项1,尽管它使程序员更容易,但实际上非常浪费(如果你没有通过使用其他类型的集合进行某种优化,如上所述)。如果你选择了部分选项,我会强烈建议从呼叫者那里抽象出来。具体来说,使重载接受List
私有,并且该方法具有单独的公共重载,而没有在其创建的列表中传递给私有重载的额外参数,然后返回该列表。这让调用者认为您正在使用类似于第一种方法的方法,同时仍然获得第二种方法的性能优势。但它仍然使开发变得复杂。
另一种选择是完全避免递归,这是我个人的偏好。当你以迭代方式而不是递归方式解决问题时,所有问题都会消失:
public static IEnumerable<Control> GetAllChildren(this Control root)
{
var stack = new Stack<Control>();
stack.Push(root);
while (stack.Any())
{
var next = stack.Pop();
foreach (Control child in next.Controls)
stack.Push(child);
yield return next;
}
}
(如果你想要特定类型的所有控件只是在这个查询的结果上调用OfType
,那么最好将“获取所有子项”的逻辑操作从“过滤集合到只是这些类型的控件“。)
答案 2 :(得分:4)
在您的具体情况下,我会推荐一个发电机:
public static IEnumerable<T> GetControlsRec<T>(Control.ControlCollection controlCollection) where T : Control
{
foreach (Control control in controlCollection)
{
if (control is T)
yield return (T)control;
if (control.HasChildren)
foreach (T descendant in GetControlsRec(control.Controls))
yield return descendant;
}
}
然后:
list.AddRange(GetControlsRec<…>(…));
(对不起,如果语法不太正确,但你明白了。)
答案 3 :(得分:3)
在一般情况下,没有一个人回答恕我直言。这实际上取决于背景和环境。
在这种特定情况下,我可能会返回一个IEnumerable而让调用者决定将其放入列表中:
public static IEnumerable<T> GetControlsRec<T>(Control.ControlCollection controlCollection)
where T : Control
{
foreach (Control control in controlCollection)
{
if (control is T)
yield return (T)control;
if (control.HasChildren)
foreach (T child in GetControlsRec(control.Controls))
yield return child;
}
}
或者更好的是,将扩展方法打到Control
本身,类似于:
public static IEnumerable<T> GetDecendentsOfType<T>(this Control c)
{
foreach (Control control in c.Controls)
{
if (control is T)
yield return (T)control;
if (control.HasChildren)
foreach (T child in control.GetDecendentsOfType<T>())
yield return child;
}
}
然后可以简单地称为:
Control myControl = Master.MakeMeAControl();
List<CheckBox> allCheckBoxesInControl = myControl.GetDecendentsOfType<CheckBox>().ToList();
答案 4 :(得分:1)
之间没有[可检测的]差异
List<int> list = new List<int>();
Foo(list);
.
.
.
void Foo( List<int> c )
{
c.Add(1) ;
return ;
}
和
List<int> list = Foo();
.
.
.
List<int> Foo()
{
List<int> c = new List<int>() ;
c.Add(1) ;
return c;
}
第一个做了一些工作,第二个没有在传递调用Foo()
的参数时完成;第二个做的工作没有完成,第一个将List<int>
从Foo()
调用回{{1}}。除此之外,基本上没有区别。
找出你想要使用的语法(以及对你的设计有意义的语法)并使用它。几乎总是,清晰度和可维护性胜过其他任何事情。这样的事情对性能的影响极不可能。
更简单,你可以完全摆脱Foo()
。类似的东西:
List<int> c = Enumerable.Range(1,1).ToList() ;
或
List<int> c = new List<int>( new int[]{1} ) ;
应该做你。
答案 5 :(得分:0)
鉴于您的额外信息,我认为您目前的方式是正确的。由于您使用递归来走树,您可以继续向下传递列表。如果你没有通过列表,那么你必须创建它(每次递归一次)。
public static List<T> resultCollection GetControlsRec<T>(Control.ControlCollection controlCollection) where T : Control
{
///Create new collection
List<T> resultCollection = new List<T>();
foreach (Control control in controlCollection)
{
if (control is T)
resultCollection.Add((T)control);
if (control.HasChildren)
resultCollection.AddRange(GetControlsRec(control.Controls, resultCollection));
}
return resultCollection;
}
答案 6 :(得分:-2)
我通常做的是
public IList<int> Foo1(IList<int> list)
{
list.Add(1);
return list;
}
对我而言,它更具可读性,因为您可以清楚地识别输入和输出。 因为对象是通过引用传递的,所以性能是相同的。
希望它有所帮助^^