编译器是否会优化掉两次创建此列表?

4
public SharpQuery Add(params HtmlNode[] elements)
{
    var nodes = new List<HtmlNode>(_context.Count + elements.Length);
    nodes.AddRange(_context);
    nodes.AddRange(elements);
    return new SharpQuery(nodes, this);
}

public SharpQuery(IEnumerable<HtmlNode> nodes, SharpQuery previous = null)
{
    if (nodes == null) throw new ArgumentNullException("nodes");
    _previous = previous;
    _context = new List<HtmlNode>(nodes);
}

我有很多函数创建一个新的List<T>,添加许多节点,然后将其传递给另一个构造函数,该构造函数使用该列表创建另一个新列表。

编译器是否足够聪明,能够找出它实际上不需要两次创建列表?


您能否考虑将问题标题更加具体化?像这样的问题标题往往会导致重复。 - Andre Luus
@Andre:抱歉,很难想到一个不显示代码但能捕捉信息的标题。 - mpen
没问题。现在好多了 :) - Andre Luus
4个回答

5

这不是“足够聪明”的问题 - 编译器只会按照指令执行;你告诉它创建多个列表,它就会创建多个列表。

然而,由于你及时释放它们,它们应该会相当干净地被收集,希望是gen-0。所以,除非你在一个紧密的循环中执行此操作,否则不必过于兴奋。

如果你想避免使用列表,你可以考虑使用LINQ的Concat方法,它允许你在不使用任何额外列表/集合等的情况下追加序列。


不创建额外的列表,如何使用concat?我知道它可以用于可枚举对象,但我无法创建一个可枚举对象...? - mpen
算了,Guffa的回答已经解决了问题。我将上下文连接起来,这不会修改上下文,只是为其提供一个可枚举的对象。很酷 :) - mpen

2

不,编译器不能进行这样的优化。

由于构造函数需要一个IEnumerable,因此您可以创建一个表达式而不是列表:

public SharpQuery Add(params HtmlNode[] elements) {
  return new SharpQuery(_context.Concat(elements), this);
}
Concat 方法会创建一个表达式,首先返回 _context 的项目,然后是 elements 的项目。当列表在构造函数中创建时,它将使用该表达式直接从 _contextelements 读取,因此集合仅创建一次。

请注意,"Union" 不同于两个列表相加,因为 "Union" 是唯一的;你是不是想说 "Concat"? - Marc Gravell
@Marc Gravell:好观点,当然我指的是Concat - Guffa

2
如果你告诉它创建一个新对象,它就会创建一个新对象。我认为没有一种优化方法可以用转换和分配来替换构造函数调用 - 编译器必须了解构造函数的作用才能以这种方式进行优化。
从技术上讲,你可以自己做 - `_context = (List)nodes;` - 这就是你想让编译器做的事情。或者更好的方式是,`_context = nodes as List ?? new List(nodes)`。但在任何情况下,列表可能会在你的类外部被修改,所以你必须确保它不会引起意外行为。
到目前为止,这似乎是过早的优化。你的代码看起来很好,我不会在看到实际性能问题之前改变任何东西。

嗯,这是一个库。在我的使用中可能没有任何性能问题,但其他人可能会有。这更多地是关于学习如何正确地做事情,而不是微观优化。 - mpen
我不知道你可以像那样转换可枚举对象...但即使我可以,我也不喜欢它,因为需要复制以防止在类外部修改。只有在通过Add()调用时,我才不需要创建新的对象。 - mpen
你可以创建一个私有构造函数,它接受一个列表并将其原样存储,还可以创建一个公共构造函数来复制成员。然后在Add()和其他你控制列表来源的地方调用私有构造函数。 - VladV

1
编译器(JIT)无法优化它。它会为您创建两个列表。问题是,这是否会导致性能问题。在考虑性能时,您需要进行测量、测量和测量。在98%的情况下,我认为这段代码不会造成任何问题。

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