Search code examples
c#memory.net-7.0asp.net-core-7.0

Purpose of `AsMemory()` applied to `byte[]` in `FileStream`


I'm revising the code and notice that I get a suggestion to use the extension method AsMemory(...). I've never seen it before nor ha a reason to consider it. Never the less, one's either learn och obsoletes, so I got intrigued.

After reading the (rather limited) docs and (equally poor) set of blogs (honestly, this one) was the only remotely relevant I found, I decided I'm too ignorant to make a judgement call on this.

This is a sample of the code used.

await using FileStream input = new(path, FileMode.Open);
byte[] buffer = new byte[1024];
long remaining = input.Length;
int index = 0;

do
{
  int read = await input.ReadAsync(buffer, 0, Config.Chunk);
  remaining -= read;
  await using FileStream output = new(destination, FileMode.CreateNew);
  await output.WriteAsync(buffer, 0, read);
} while (remaining > 0);
input.Close();

What's the practical difference between those two readings from a stream? Where can I find more relevant and reliable information?

int read1 = await input.ReadAsync(buffer, 0, Config.Chunk);
int read2 = await input.ReadAsync(buffer.AsMemory(0, Config.Chunk));

I strongly hope that it has to do with performance optimization and/or thread management. However, I also hope to be handsome and funny, which was a mistake...

The same question goes for the writing operation, I guess.

await output.WriteAsync(buffer, 0, read);
await output.WriteAsync(buffer.AsMemory(0, read));

Solution

  • None, in this case.

    The overload does exactly the same thing as your own code: it creates a ReadOnlyMemory from the buffer array.

    Source code is here: (it eventually reaches a class called FileStreamStrategy)

    public sealed override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
        WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).AsTask();
    
    public sealed override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
    {
        long writeOffset = CanSeek ? Interlocked.Add(ref _filePosition, source.Length) - source.Length : -1;
        return RandomAccess.WriteAtOffsetAsync(_fileHandle, source, writeOffset, cancellationToken, this);
    }
    

    Having said that, in many cases it is useful. This is normally where you would get a Memory from somewhere else, for example from an unmanaged memory block, or from some kind of array pool.