Review from the future
This review if from the future so you don't see it right now, it isn't there yet ;-)
I will write it in a couple of weeks or months when the new
System.Memory package is official. But if you think you will like it then, you can leave your future vote anyway ;-P
The components necessary for the changes I describe here are not released yet and the new features are still in beta but they are very interesing and worth trying.
So, we finally can use the new
[ReadOnly]Memory<T> types. But what are they?
Span<T> is a new type we are adding to the platform to represent contiguous regions of arbitrary memory, with performance characteristics on par with T. Its APIs are similar to the array, but unlike arrays, it can point to either managed or native memory, or to memory allocated on the stack. 
One of the APIs that makes it so useful is the
Slice method that is like a
string but without copying anything.
The full API surface of Span is not yet finalized 
Slice however should stay.
Unfortunatelly we cannot use this type here becuase
Span is a ref-like type as it contains a ref field, and ref fields can refer not only to the beginning of objects like arrays, but also to the middle of them. 
These references are called interior pointers, and tracking them is a relatively expensive operation for the .NET runtime’s garbage collector. As such, the runtime constrains these refs to only live on the stack, as it provides an implicit low limit on the number of interior pointers that might be in existence. 
As a result, Span instances can only live on the stack, not on the heap. This means you can’t box spans. 
but the most import point against it in our use-case is that
you can’t use Span as a generic argument, as instances of that type argument could end up getting boxed or otherwise stored to the heap (and there’s currently no “where T : ref struct” constraint available). 
and we need something that we can use for
IEnumerable<T> to enumerate paths.
Memory<T> to the rescue
Span<T> can’t be stored to the heap and thus can’t be persisted across asynchronous operations, what’s the answer?
This can be use just like a
Span<T> so we can put it inside
IEnumerable<T> and rewrite the method for enumerating paths like this:
public static IEnumerable<ReadOnlyMemory<char>> EnumeratePaths(string path, SearchOption searchOption)
.EnumerateFiles(path, "*", searchOption)
.Concat(Directory.EnumerateDirectories(path, "*", searchOption))
.Select(name => new ReadOnlyMemory<char>(name.ToArray()));
It concatenates the results of
EnumerateDirectories and returns them as
In order to get the relative path, we neither use the
Replace nor the
Substring methods but instead the
public static ReadOnlyMemory<char> RelativePath(this ReadOnlyMemory<char> path, int relativePathStart)
return path.Slice(relativePathStart, path.Length - relativePathStart);
With it, we can create any relative path we want without copying strings:
static void Main(string args)
var rootPath = @"C:\foo\bar";
foreach (var path in EnumeratePaths(rootPath, SearchOption.AllDirectories))
String slicing might be in some scenarios a very expensive operation so it's good to know that there is a new feature that can significant improve its performance, when necessary.
System.String.Substring is the main .NET API for creating slices of a string, but the API is inefficient as it creates a new string to represent the slice and copies the characters from the original string to the new string slice. Because of this inefficiency, high performance servers shy away from using this API, where they can (in their internals), and pay the cost in the publicly facing APIs. 
There are also other use-cases that can be optimized with the new types like parsing or formatting.
The types, methods, runtime optimizations, and other elements discussed here are on track to being included in .NET Core 2.1. 
After that, they are expected to make their way into the .NET Framework. Let's hope they will...
- C# - All About Span: Exploring a New .NET Mainstay
Span<T> - dotnet/corefxlab