【问题标题】:Is there any performance hit to setting a list as itself?将列表设置为自身是否会对性能造成影响?
【发布时间】:2015-09-10 13:19:01
【问题描述】:

如果尚未实例化,我有一个可能为 null 的列表,我希望在调用 GetList() 时能够返回现有列表或创建列表然后返回。这看起来更干净:

private List<object> m_objects;
public List<object> GetList()
{
    m_objects = m_objects ?? new List<object>();
    return m_objects;
}

但是将列表设置为自身是否会影响性能,或者 C# 是否意识到这没有必要?

替代方案是:

private List<object> m_objects;
public List<object> GetList()
{
    if(m_objects != null) 
    {
        return m_objects;
    }
    m_objects = new List<object>();
    return m_objects;
}

显然不是世界末日,但我仍然很好奇。

【问题讨论】:

  • 或者简单地说:private readonly List&lt;object&gt; m_objects = new List&lt;object&gt;(); public List&lt;object&gt; GetList(){return m_objects;}
  • 与任何性能问题一样,您当然可以简单地对其进行测试。虽然我严重怀疑会有很大的不同。
  • 对于初学者,如果我们不打算每次都使用它,我们不会总是希望新的对象。我会称之为延迟初始化

标签: c# performance list


【解决方案1】:

使用Lazy&lt;T&gt;:

private Lazy<List<object>> m_objects = new Lazy<List<object>>();
public List<object> GetList()
{
    return m_objects.Value;
}

解决性能问题。在这里担心性能是过早的优化。您应该首先对其进行编码以使其正常工作,然后如果您发现任何与性能相关的问题,请对其进行分析并进行优化。

【讨论】:

  • 懒惰是做什么的?至少包含的任何参考资料作为答案恕我直言会更好。
  • @BeytanKurt 如果您想了解更多信息,可以查看该类型的文档。
  • 在这里担心性能是相当过早的优化。
  • @PeterSchneider 而且这里没有禁用它。我不认为它会戏剧性地更贵。它可能根本不重要,但在最坏的情况下,OP 的代码是对另一个注册表值执行多余的分配,因此它的操作速度与您可能拥有的一样快。我确信所有选项都足够好,但我毫不怀疑这会比其他所有选项都慢(稍微低一些)。
  • 当然可以,但请考虑两个线程同时获得价值的情况。 Lazy 确保它将是相同的值,而不是 2 个不同的列表。 Lazy 就是这种情况。内部对象的线程安全是另一个问题。
【解决方案2】:

这是完全有效的:

private List<string> items;
public List<string> Items { get { return items ?? (items = new List<string>()); } }

注意?? (items = 的区别。没有性能损失,因为如果它具有非空值,它会在 ?? 处被布尔短路。


正如所指出的,如果你想使用你的代码,那么是的,它确实会在每次创建新元素时对性能造成影响。

【讨论】:

  • 使用 object 不是您通常想做的事情 - 例如 dynamicLazy 更好。
  • 这实际上并没有回答所提出的问题。
  • 不要认为问题要求替代方案,它会询问特定样式是否会产生开销
  • 添加“是的,确实如此”后,既回答了问题,又提供了一个简单而聪明的选择。
  • 编辑后:OP 的代码中没有“每次都创建对象”,还是我遗漏了什么? reference 被分配给自己,除非分配被优化掉(这可能是 OP 所要求的)。
【解决方案3】:

由于问题特别要求性能,这在调用GetList() 时为您提供最佳性能,并且它是线程安全的:

private readonly List<object> m_objects = new List<object>();
public List<object> GetList()
{
    return m_objects;
}

另一个使它成为线程安全的选择是使用Lazy&lt;T&gt;。这会延迟new List&lt;object&gt;(),但代价是始终执行new Lazy&lt;List&lt;object&gt;&gt;(),并在GetList() 方法中增加额外开销。

【讨论】:

  • 这可能是最好的用法,只要没有很多 m_objects 通过创建很多它的父类来创建。如果只针对父类的一小部分调用 GetList(),这将创建大量空列表,并不必要地运行大量列表构造函数。
【解决方案4】:

可能会对性能造成最小的影响,因为您基本上是将m_objects 中保存的引用分配给m_objects 字段。这实际上只是复制了一个 64 位或 32 位指针。您不会复制所有数据,只是对对象实例的引用(或 null)。

对于您的代码,我认为使用 ??只要您不担心多线程,引文就非常整洁和足够。如果你是,那么 Lazy 建议 @Michał Kędrzyński 建议是更好的方法,因为你不需要编写所有这些。

唯一的例外是,如果由于 UI 限制,您需要确保您在 WPF 中的 Dispatcher 线程或 Winforms 中的 UI 线程上。

编辑

只是为了纠正我自己的答案,如果您的泛型类型无论如何都不是线程安全的(例如,List 不是线程安全的),那么使用 Lazy 是没有实际意义的

【讨论】:

  • 好点我一直在离开 cmets 所以自动对我的做同样的事情,而不是像往常一样编辑!糟糕,但感谢您指出
猜你喜欢
  • 2013-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-07
  • 2021-10-30
  • 2017-11-23
  • 1970-01-01
  • 2010-12-19
相关资源
最近更新 更多