【问题标题】:How to use StreamReader.ReadAsync(Memory<Char>, CancellationToken)如何使用 StreamReader.ReadAsync(Memory<Char>, CancellationToken)
【发布时间】:2018-12-02 01:46:55
【问题描述】:

我正在尝试使用接受Memory&lt;T&gt;StreamReader.ReadAsync(Memory, CancellationToken) overload。 以下是示例代码:(.NET Core 2.1)

static async Task Main(string[] args)
{
    string code = "Hello, World";
    var memoryStream = Encoding.UTF8.GetBytes(code.ToCharArray(), 0, code.Length);
    using (var stream = new MemoryStream(memoryStream))
    using (var reader = new StreamReader(stream))
    {
        // var text = await reader.ReadLineAsync();
        // Console.WriteLine(text); -> Outputs: Hello, World

        Memory<char> memory = new Memory<char>();
        await reader.ReadAsync(memory);

        Console.WriteLine("Memory<char> length:" + memory.Length);
    }

}

代码尝试通过StreamReader 读取字符串内容并填充Memory&lt;char&gt;。但是,对于 Memory&lt;char&gt; 长度,代码一直输出 0。

使用此方法填充Memory&lt;char&gt; 的正确方法是什么?

我的.csproj文件内容:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <LangVersion>7.2</LangVersion>
  </PropertyGroup>
</Project>

【问题讨论】:

  • 你是在写还是从内存中读取 await reader.ReadAsync(memory);?
  • 尝试从流中读取到内存。
  • 作为测试,删除 async 的所有内容,没有它你的代码可以工作吗?
  • @CodingYoshi 不,它仍然不起作用。

标签: c# .net-core


【解决方案1】:

可行的解决方案是使用缓冲区初始化内存并注意 ReadAsync 的返回值,以便您知道您读取了多少个字符(而不是字节)。

您确实遇到了 Memory 是一个结构体的问题,它始终允许您创建不带参数的实例,尽管从 API 使用的角度来看这是不允许的。

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace ReadMemory
{
    class Program
    {
        public static async Task Main(string[] args)
        {
            string code = "Hello, World";
            var memoryStream = Encoding.UTF8.GetBytes(code.ToCharArray(), 0, code.Length);
            using (var stream = new MemoryStream(memoryStream))
            using (var reader = new StreamReader(stream))
            {

                Memory<char> memory = new Memory<char>(new char[1024]);  // Init with backing buffer. Otherwise you are trying to read 0 bytes into a zero sized buffer. 
                int charsRead = await reader.ReadAsync(memory);

                Console.WriteLine($"Memory<char> len: {memory.Length}, bytes read: {charsRead}");
            }

        }
    }
}

回答你第二个问题。是的,您可以使用 ReadAsync(char[] ...) 代替内存。它们是等效的,因为在引擎盖下它作为 System.Memory 被进一步传递,然后当涉及到实际读数转换为 Span 时。 见

public virtual System.Threading.Tasks.Task<int> ReadAsync(char[] buffer, int index, int count)
{
    return this.ReadAsyncInternal(new Memory<char>(buffer, index, count), default(System.Threading.CancellationToken)).AsTask();
}

【讨论】:

  • 感谢您的回答。如果我需要传递后备缓冲区,我可能会使用reader.BaseStream.Length 而不是1024 来初始化它。其次,您不认为引入缓冲区(char数组)意味着我可以使用接受char数组的the other overload,然后自己将Memory包裹起来吗?
  • @Michael:是的,你可以。请参阅我的更新答案。为什么要首先使用 Memory
  • 我正在使用Memory&lt;T&gt; 模拟场景,例如我通过网络接收内容。所以我收到了NetworkStream 并且需要处理数据。如果使用此 API 的唯一方法是传递缓冲区,我真的很难看到 Memory&lt;T&gt; 重载的好处,为什么这个重载存在?如果无论如何我都需要创建缓冲区,我会创建它,传递给将内容写入缓冲区的方法,然后将其包装在 Memory&lt;T&gt; 中并使用它。
  • Memory 只是一个包含起始索引和长度的数组部分的包装器。这样您就可以共享一个大数组并使用不同的 Memory 实例写入相应的部分。他们需要添加 Memory 因为 Span 只能存在于线程堆栈上,并且永远不能成为类成员。
猜你喜欢
  • 2013-02-10
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 2015-03-27
  • 1970-01-01
  • 2015-09-01
  • 1970-01-01
相关资源
最近更新 更多