如何将Memory <T>转换为另一个

moi*_*ien 5 c#

我们可以使用MemoryMarshal.Cast方法重载Span<T>ReadOnlySpan<T>另一个方法.喜欢 :

Span<byte> span = stackalloc byte[4];
var singleIntSpan = MemoryMarshal.Cast<byte, int>(span);
Run Code Online (Sandbox Code Playgroud)

但有没有办法投射Memory<T>到另一个?例如投Memory<byte>Memory<ushort>.

Mar*_*ell 7

你不能直接做到; 但是,如果你真的需要,你可以创建一个自定义MemoryManager<T>(大概实际上是一个MyMemoryManager<TFrom, TTo> : MemoryManager<TTo>执行转换作为GetSpan()覆盖的一部分.这有点不重要,并且需要另一个分配 - 不像一个Span<T>免于分配的强制转换.

如果你需要一个具体的例子,我可以鞭打一个(我实际上在一些现有的代码中这样做),但是:说实话,你可能想重新考虑这个场景.

编辑:这样的事情:

using System;
using System.Buffers;
using System.Runtime.InteropServices;

class Program
{
    static void Main()
    {
        Memory<byte> bytes = new byte[1024];

        Memory<ushort> typed = Utils.Cast<byte, ushort>(bytes);
        Console.WriteLine(typed.Length); // 512

        // note CPU endianness matters re the layout
        typed.Span[0] = 0x5432;
        Console.WriteLine(bytes.Span[0]); // 50 = 0x32
        Console.WriteLine(bytes.Span[1]); // 84 = 0x54
    }
}

static class Utils
{
    public static Memory<TTo> Cast<TFrom, TTo>(Memory<TFrom> from)
        where TFrom : unmanaged
        where TTo : unmanaged
    {
        // avoid the extra allocation/indirection, at the cost of a gen-0 box
        if (typeof(TFrom) == typeof(TTo)) return (Memory<TTo>)(object)from;

        return new CastMemoryManager<TFrom, TTo>(from).Memory;
    }
    private sealed class CastMemoryManager<TFrom, TTo> : MemoryManager<TTo>
        where TFrom : unmanaged
        where TTo : unmanaged
    {
        private readonly Memory<TFrom> _from;

        public CastMemoryManager(Memory<TFrom> from) => _from = from;

        public override Span<TTo> GetSpan()
            => MemoryMarshal.Cast<TFrom, TTo>(_from.Span);

        protected override void Dispose(bool disposing) { }
        public override MemoryHandle Pin(int elementIndex = 0)
            => throw new NotSupportedException();
        public override void Unpin()
            => throw new NotSupportedException();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你真的想支持锁定/解锁,这应该是可能的-你只需要计算的相对范围和偏移,从竞争TFrom/ TTo,虽然-大概使用Unsafe.SizeOf<T>等,并使用MemoryMarshal.TryGetMemoryManager获得的底层存储管理器(如果有-请注意,裸阵列没有内存管理器).除非你要广泛测试这个选项,否则投掷可能比错误更安全.

  • @MichaelRandall不,不是; 基本上,`Memory <T>`只是一个偏移+计数和一个`object`引用.那个`object`*可以是`T []`,但它也可以是`MemoryManager <T>`.如果是后者,那么*actual*span是`((MemoryManager <T>)obj).GetSpan().Slice(offset,count)`; 如果它是一个数组,那么它是`new Span <T>((T [])obj,offset,count)`.没有比这更好的了.任何所有权等都是完全独立的. (3认同)
  • 哇,这太棒了,我一直在绞尽脑汁想办法解决这个问题,有很多处女在这个黑魔法中被牺牲了! (2认同)
  • @moien 好问题,但我的看法是:完整的 pin/unpin 实现需要比我为这个示例编写的代码多得多的代码,其中包括互锁计数器、跟踪的“MemoryHandle”以及用于比较的所有数学运算。 `TFrom`/`TTo` 的大小。所以......“读者练习” (2认同)