【问题标题】:Return always new list from a function or not?是否总是从函数返回新列表?
【发布时间】:2015-09-14 16:44:54
【问题描述】:

我需要为一个类创建一个公共函数,该函数返回一个 List 的项目,例如 List(of employee)。

这个函数会在这个类之外被频繁调用。

从内存消耗来看,是不是更好:

  1. 在此函数中始终初始化新列表,向其中添加项目并返回此列表,或者;
  2. 在字段中存储一个列表,从中清除项目,添加新项目并返回此列表

代码示例:

1.

public List<employee> GetItems()
{
    List<employee> list = new List<employee>();
    list.Add(new employee());
    list.Add(new employee());
    ....
    return list;
}

2.

private List<employee> _list = new List<employee>();
public List<employee> GetItems()
{
    _list.Clear();
    _list.Add(new employee());
    _list.Add(new employee());
    ...
    return _list;
}

就内存消耗而言,上述之一是否更受欢迎?在什么情况下应该使用上述一种而不是另一种?

【问题讨论】:

  • 两者看起来都很可疑——你不应该返回一个可变集合。特别是在第二种情况下 - 有人可能会修改列表,并且您会为不同的调用得到不同的结果。使用IEnumerable&lt;T&gt;。话虽如此,这似乎有点过于宽泛和基于意见。
  • 我不认为从 API 的角度来看 2。当另一个类调用GetItems 时,任何从GetItems 获取引用的地方都会清除并重置其列表?奇怪且很可能容易出错。也许有一个名字可以使这有意义,但我想不出一个。不过,从您的示例来看,也许您应该查看iterators
  • 大概您的意思是选项 2 中的 return _list,在这种情况下您正在破坏封装。像这里的其他人一样,我会远离那个选项。
  • @BartoszKP 如果集合是不可变的,则选项二是不可能的。
  • @BartoszKP 如果想要不变性并且底层结构是List&lt;T&gt;,我可能宁愿选择IReadonlyList&lt;T&gt;

标签: c# .net


【解决方案1】:

第二个选项将使用更少的内存。可能会少很多(由于您可能还没有考虑到某些行为)。

第一个示例返回一个实际的新对象给调用者。由于局部变量立即超出范围,因此对象的生命周期将由调用者确定。所以它会使用更多的内存,因为创建了更多的对象,即使它们很快被销毁,GC也不会立即收集它们。

第二个选项只有一个对象,因此使用更少的内存。 然而这意味着所有的调用者都将指向同一个对象。因此,每次您清除和添加时,它都会影响所有以前的调用者,除非他们制作了副本。此外,如果多个线程使用此类,则存在很大的线程危险。

第二个选项,虽然它使用较少的内存,但是非常危险的代码,我会非常犹豫是否使用它。

【讨论】:

  • 这是错误的。事实上,两个选项将使用相同数量的内存,因为消耗内存的不是列表本身,而是单独分配的项目正在消耗内存。实际列表将大致占用sizeof(reference) * itemCount,与Employee 实例的大小相比可以忽略不计。鉴于这两个选项总是创建新的Employees,因此在给定相同消费者的情况下,两者都将使用相同数量的内存。
  • @BorisB。完全不正确,虽然列表的大小可以忽略不计,但它 not 0。此外,第一个选项可能会创建更多持续存在的 Employee 对象,除非有其他东西引用它们分别在Clear 之后的某个时间被收集。我完全同意内存是这段代码最不关心的问题,但第二个几乎肯定会使用更少(在一定程度上)。
  • @BradleyDotNET 虽然会导致更大的内存碎片。
  • @BartoszKP 毫无疑问。
【解决方案2】:

永远不要做选项二。请记住,您返回的是相同的参考。因此,当您清除列表时,其他人可能正在尝试迭代它。这将导致异常。

此外,在您返回列表后,您无法控制其他人对其执行的操作,例如添加/删除项目将影响从函数中获取列表的所有调用者。

【讨论】:

    【解决方案3】:

    这两个例子并不等同。在第二个示例中,您将返回对同一列表的引用。

    第一个方法总是会返回一个唯一的对象,这大概就是你想要的。

    【讨论】:

      【解决方案4】:

      正如许多受访者所说,选择选项 1。同样正如 @BartoszKP 所说,我可能会返回 IEnumerable。您可以通过缩短初始化来节省一些击键。可以将代码表示如下:

      public IEnumerable<employee> GetItems()
      {
        return new List<employee> { 
          new employee("Alan"),
          new employee("Bob"),  
          new employee("Carla")
        }; 
      }
      

      【讨论】:

        猜你喜欢
        • 2010-11-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多