代码之家  ›  专栏  ›  技术社区  ›  Orion Edwards

如何在避免不必要的复制的同时从列表中获取Span<T>?

  •  11
  • Orion Edwards  · 技术社区  · 6 年前

    List<T> 包含一些数据。我想把它传递给一个接受 ReadOnlySpan<T> .

    List<T> items = GetListOfItems();
    // ...
    void Consume<T>(ReadOnlySpan<T> buffer)
    // ...
    Consume(items??);
    

    byte 但这并不重要。

    我知道我可以用 .ToArray() 在列表中,构造一个跨度,例如。

    Consume(new ReadOnlySpan<T>(items.ToArray()));
    

    列表<T> 是根据 T[] 在幕后,所以在理论上是可能的,但在实践中我看不到?

    2 回复  |  直到 6 年前
        1
  •  3
  •   Orion Edwards    6 年前

    感谢您提供的所有评论,这些评论解释了没有实际的方法可以做到这一点,以及暴露列表中的内部数组可能会导致不良行为和断开的跨度。

    我最终重构了代码,不使用列表,而只是首先生成跨度。

    void Consume<T>(ReadOnlySpan<T> buffer)
    // ...
    
    var buffer = new T[512]; 
    int itemCount = ProduceListOfItems(buffer); // produce now writes into the buffer
    
    Consume(new ReadOnlySpan<T>(buffer, 0, itemCount);
    

    我选择在一次过度分配缓冲区的情况下做出明确的权衡,以避免以后生成额外的副本。

    在我的特定情况下,我可以做到这一点,因为我知道在项目计数上会有一个最大的上限,并且过度分配不是什么大问题,然而,这里似乎没有泛化,也不会有人增加,因为这将是危险的。

    与以往一样,软件性能是一门权衡(希望是有利的)的艺术。

        2
  •  1
  •   usr    6 年前

    你可以自己写 CustomList<T> 它公开了底层数组。然后在用户代码上正确使用这个类。

    自定义列表<T> 不会意识到任何 Span<T> 跨度<T> 不应让列表执行任何操作来创建新数组或在旧数组中创建未定义的数据。

    C++标准库允许用户代码获得直接指针。 vector<T> 后备存储器。他们记录了安全的条件。例如,调整大小会使其不安全。

    MemoryStream . 这个类允许您访问底层缓冲区,实际上不安全的操作是可能的。