How can I get a Span from a List while avoiding needless copies?

后端 未结 3 1555
天涯浪人
天涯浪人 2021-02-04 01:29

I have a List containing some data. I would like to pass it to a function which accepts ReadOnlySpan.

List i         


        
相关标签:
3条回答
  • 2021-02-04 01:43

    Thanks for all the comments explaining that there's no actual way to do it and how exposing the internal Array inside List could lead to bad behaviour and a broken span.

    I ended up refactoring my code not to use a list and just produce spans in the first place.

    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);
    

    I'm chosing to make the explicit tradeoff of over-allocating the buffer once to avoid making an extra copy later on.

    I can do this in my specific case because I know there will a maximum upper bound on the item count, and over-allocating slightly isn't a big deal, however there doesn't appear to be a generalisation here, nor would one ever get added as it would be dangerous.

    As always, software performance is the art of making (hopefully favorable) trade-offs.

    0 讨论(0)
  • 2021-02-04 01:48

    In .Net 5.0, you can use CollectionsMarshal.AsSpan() (source, GitHub isue) to get the underlying array of a List<T> as a Span<T>.

    Keep in mind that this is still unsafe: if the List<T> reallocates the array, the Span<T> previously returned by CollectionsMarshal.AsSpan won't reflect any further changes to the List<T>. (Which is why the method is hidden in the System.Runtime.InteropServices.CollectionsMarshal class.)

    0 讨论(0)
  • 2021-02-04 02:03

    You can write your own CustomList<T> that exposes the underlying array. It is then on user code to use this class correctly.

    In particular the CustomList<T> will not be aware of any Span<T> that you can obtain from the underlying backing array. After taking a Span<T> you should not make the list do anything to create a new array or create undefined data in the old array.

    The C++ standard library allows user code to obtain direct pointers into vector<T> backing storage. They document the conditions under which this is safe. Resizing makes it unsafe for example.

    .NET itself does something like this with MemoryStream. This class allows you access to the underlying buffer and indeed unsafe operations are possible.

    0 讨论(0)
提交回复
热议问题