【问题标题】:How can I create a Memory<T> from a Span<T>?如何从 Span<T> 创建 Memory<T>?
【发布时间】:2018-06-29 19:53:50
【问题描述】:

除了string 版本之外,我正在尝试重载解析方法以使用ReadOnlySpan&lt;char&gt; 参数。问题是该实现使用Dictionary&lt;string, T&gt; 进行解析逻辑。

我尝试将其切换为Dictionary&lt;ReadOnlySpan&lt;char&gt;, T&gt;,但当然这不起作用,因为ReadOnlySpan&lt;char&gt; 不允许作为通用参数,因为它是一个仅堆栈对象。然后我将其切换为使用允许的ReadOnlyMemory&lt;char&gt;。然后我实现了一个基本的Ordinal 比较器,但现在我无法从ReadOnlySpan&lt;char&gt; 参数创建ReadOnlyMemory&lt;char&gt;。这可能吗?

更新

这似乎是不可能的。为了支持我在上面发布的场景,我将更改字典以具有一个 int 键,它是 ReadOnlySpan&lt;char&gt; 的哈希码,并使值成为一个列表,其中包含嵌入在元素中的字符串,并且必须手动解析哈希码碰撞。

【问题讨论】:

  • System.MemoryExtensions 类中应该有一个 AsMemory() 方法来执行此操作,因为在移动设备上无法测试
  • 很遗憾,AsMemory() 只接受strings、数组和ArraySegment&lt;T&gt;s。
  • 由于 Spans 存在于堆栈中,您必须使用 ToArray() 方法将其复制到堆中。数组被隐式转换为 ReadOnlyMemory

标签: c# .net .net-core


【解决方案1】:

TL;DR:

您可以将Memory&lt;T&gt; 转到Span&lt;T&gt;,但不能反过来。

背景:

有一篇内容丰富的 2018 年 MSDN 杂志文章文章介绍了 2018 年 MSDN Magazine article 中的 Span&lt;T&gt;

Span&lt;T&gt; 实例只能存在于堆栈上,不能存在于堆上。 这意味着您不能装箱(因此不能将 Span&lt;T&gt; 与现有的反射调用 API 一起使用,例如,因为它们需要装箱)。这意味着你不能在类中拥有Span&lt;T&gt; 字段,甚至在非类似引用的结构中。这意味着您不能在它们可能隐式成为类上的字段的地方使用跨度,例如通过将它们捕获到 lambda 或作为异步方法或迭代器中的局部变量(因为这些“局部变量”最终可能成为编译器生成的字段状态机。)这也意味着你不能使用 Span&lt;T&gt; 作为通用参数,因为该类型参数的实例最终可能会被装箱或以其他方式存储到堆中(并且目前没有可用的“where T : ref struct”约束) .

...

你可以从一个数组中创建一个Memory&lt;T&gt; 并像你一样对它进行切片 会是一个跨度,但它是一个(非 ref-like)结构并且可以存在于 堆。然后,当你想做同步处理时,你可以得到一个 Span&lt;T&gt; 来自它,例如:

static async Task<int> ChecksumReadAsync(Memory<byte> buffer, Stream stream)
{
  int bytesRead = await stream.ReadAsync(buffer);
  return Checksum(buffer.Span.Slice(0, bytesRead));
  // Or buffer.Slice(0, bytesRead).Span
}
static int Checksum(Span<byte> buffer) { ... }

我认为他的文章比我自己写答案更清楚。

【讨论】:

  • 请把短字放在最前面,节省别人的时间。
  • 很好奇Memory&lt;T&gt;public Span&lt;T&gt; Span { get; } 属性,但没有implicit operator Span&lt;T&gt;( Memory&lt;T&gt; self ) 运算符,不像我们看到的Span&lt;T&gt;ReadOnlySpan&lt;T&gt;
【解决方案2】:

如果Tunmanaged type,您可以从Span&lt;T&gt; 转到Memory&lt;T&gt;。但请注意,您正在创建的 Memory&lt;T&gt; 对象应“手动”强制执行 C# 编译器在 Spans 上保证的所有约束。

// data is Span<T> or ReadOnlySpan<T>
fixed (T* pointer = &data.GetPinnableReference())
{
     using var memoryManager = new UnmanagedMemoryManager<T>(pointer, data.Length);
     var memory = memoryManager.Memory;
     // you must use the memory object inside this fixed block!
}

UnmanagedMemoryManager 可以在 nuget 包Pipelines.Sockets.Unofficial 中找到(我不维护这个包)。但是,如果您希望以不同的方式构建它,您也可以编写非托管 MemoryManager&lt;T&gt;your own implementation

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-14
    • 2018-12-24
    • 2018-07-24
    • 1970-01-01
    • 2019-08-04
    • 2020-01-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多