Runtime: [Api Proposal] Add List<T> AsSpan to CollectionsMarshal

Created on 4 Aug 2018  ·  19Comments  ·  Source: dotnet/runtime

Its not a "safe" operation, though it is a useful one to gain access to the prebuilt array; without first copying using .ToArray()

namespace System.Runtime.InteropServices
{
    public partial static class CollectionsMarshal
    {
        public Span<T> AsSpan(List<T> list);
        public Memory<T> AsMemory(List<T> list);
    }
}

/cc @jkotas

api-approved area-System.Collections

Most helpful comment

Video

Approved shape:

```C#
namespace System.Runtime.InteropServices
{
public partial static class CollectionsMarshal
{
public static Span AsSpan(List list);
public static ReadOnlySpan AsReadOnlySpan(List list);

    // We don't want this one:
    // public Memory<T> AsMemory(List<T> list);
}

}
```

All 19 comments

I do not think this should be on MemoryMarshal. This should be on a new one, like CollectionsMarshal.

This should not be an extension method. No other methods on *Marshal are extension methods to make you do more typing.

The PowerCollections in corefxlab would be a good place to experiment with this: https://github.com/dotnet/corefxlab/issues/2406

Updated

The PowerCollections in corefxlab would be a good place to experiment with this

Would then need to go via private reflection?

@jkotas is reflection what you have in mind?

@benaadams curious, do you happen to have a motivating scenario?

curious, do you happen to have a motivating scenario?

List<T> is a very convenient datastructure for creating an array of an unknown size. However, to use it as an array you need to copy the entire contents again with .ToArray().

AsSpan/AsMemory would give the "array" correctly bounded (size of List rather than size of backing array); without the copy.

@jkotas is reflection what you have in mind?

I had in mind all the different list and array builders in "Common". They are all variants of List with different performance characteristics.

All internal though 😢

Would it make sense to propose a "ListBuilder"/"SpanBuilder" type then? It could have roughly the same surface area as List, along with an AsSpan/AsMemory method. Once those are called, mutation is no longer allowed.

@ahsonkhan

@safern this does not seem to me essential in order to ship 3.0. Please correct me if I'm wrong.

any updates, just wondering? Sounds like a great API :smiley:

@benaadams We understand that this a logical extension of the current implementation of the type. Could you list some compelling scenarios? We can always come up with potential scenarios.

Creating a Memory/Span (Array-like) of an unknown upfront size. List<T> is currently the main builder type; but getting an array from it .ToArray() causes a secondary allocation.

The alternative would be to expose an alternative builder type https://github.com/dotnet/corefx/issues/31597#issuecomment-413059736

FYI, it's currently being discussed here.

I missed this when I had a list and wanted to do something (been a while... can't remember what the exact situation was) which could be vectorised (since you need to take pointer of the underlying array). It didn't have to be exposed as a Span... but just my thought.

Video

Approved shape:

```C#
namespace System.Runtime.InteropServices
{
public partial static class CollectionsMarshal
{
public static Span AsSpan(List list);
public static ReadOnlySpan AsReadOnlySpan(List list);

    // We don't want this one:
    // public Memory<T> AsMemory(List<T> list);
}

}
```

Re-opening until API is exposed from the ref assembly.

The “approved shape” wasn’t completely implemented with https://github.com/dotnet/coreclr/pull/26867. Should we update the “approved shape” above so it is in sync with the code?

Re-flagging as api-ready-for-review to address the AsReadOnlySpan change and also do a quick review on the nullable annotations change (see https://github.com/dotnet/coreclr/pull/26903).

```C#
namespace System.Runtime.InteropServices
{
public partial static class CollectionsMarshal
{
// Updated with nullable annotation
public static Span AsSpan(List? list);

    // Since the AsSpan(...) overload is no longer changing the internal version,
    // we don't need this one anymore
    // public static ReadOnlySpan<T> AsReadOnlySpan(List<T> list);

    // We don't want this one:
    // public Memory<T> AsMemory(List<T> list);
}

}
```

Video

We decided to not increment the version, which is why we removed the AsReadOnlySpan() overload.

C# namespace System.Runtime.InteropServices { public partial static class CollectionsMarshal { public static Span<T> AsSpan(List<T>? list); } }

Was this page helpful?
0 / 5 - 0 ratings