Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
107 views
in Technique[技术] by (71.8m points)

c# - How can I cast Memory<T> to another

We can cast Span<T> and ReadOnlySpan<T> to another using MemoryMarshal.Cast method overloads. Like :

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

But is there any way to cast Memory<T> to another? for example cast Memory<byte> to Memory<ushort>.

question from:https://stackoverflow.com/questions/65905961/how-to-convert-readonlymemorychar-to-readonlymemorybyte

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can't do it directly; however, if you really need, you can create a custom MemoryManager<T> (presumably actually a MyMemoryManager<TFrom, TTo> : MemoryManager<TTo> that performs the cast as part of the GetSpan() override. This is slightly non-trivial, and demands another allocation - unlike a Span<T> cast, which is allocation-free.

If you need a concrete example of that, I can whip one up (I actually do exactly this in some existing code), but: to be honest, you probably want to reconsider the scenario instead.

Edit: something like this:

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();
    }
}

If you really want to support pin/unpin, that should be possible - you'll just need to compute the relative ranges and offsets from the competing TFrom/TTo, though - presumably using Unsafe.SizeOf<T> etc, and using MemoryMarshal.TryGetMemoryManager to get the underlying memory manager (if one - note that naked arrays don't have a memory manager). Unless you're going to extensively test that option, throwing is probably safer than getting it wrong.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...