在不装箱的情况下将字节数组复制到通用类型

Copy Byte Array into Generic Type without Boxing

我正在开发 C# class,其中我需要能够接收字节数组并将其复制到相同大小的通用变量。在 C/C++ 中,这样的事情(复制)很容易,但在 C# 中就没那么容易了。

MyClass<T>
{
  public T Value = default(T);

  public MyClass(byte[] bytes)
  {
    // how to copy `bytes` into `Value`?
  }
}

我不想使用拳击。有没有办法使用封送处理、反射或 unmanaged/unsafe 代码来做到这一点?


我确实找到了 ,但唯一建议的答案无效 因为它使用装箱

如果您使用的是最新的 .NET,您可以为此使用 Span<T> (System.Buffers):

class MyClass<T> where T : struct
{
    public T Value = default(T);

    public MyClass(byte[] bytes)
    {
        Value = MemoryMarshal.Cast<byte, T>(bytes)[0];
    }
}

您还可以在最近的 C# 版本中使用 unsafe(针对 T : unmanaged 约束):

class MyClass<T> where T : unmanaged
{
    public T Value = default(T);

    public unsafe MyClass(byte[] bytes)
    {
        fixed (byte* ptr = bytes)
        {
            Value = *(T*)ptr; // note: no out-of-range check here; dangerous
        }
    }
}

您可以使用Unsafe.*方法在这里做一些事情(System.Runtime.CompilerServices.Unsafe);例如(注意没有限制):

class MyClass<T>
{
    public T Value = default(T);

    public unsafe MyClass(byte[] bytes)
    {
        T local = default(T);
        fixed (byte* ptr = bytes)
        {
            Unsafe.Copy(ref local, ptr); // note: no out-of-range check here; dangerous
        }
        Value = local;
    }
}

如果要检查越界问题:

if (bytes.Length < Unsafe.SizeOf<T>())
    throw new InvalidOperationException("Not enough data, fool!");

或者如果您有 T : unmanaged 约束,则可以使用 sizeof(T)Span<T> 解决方案(第一个)不需要这个,因为在那种情况下原始 Cast<byte, T> 将产生长度为零的跨度,并且 [0] 将适当地抛出。


认为这应该也行!

public unsafe MyClass(byte[] bytes)
{
    Value = Unsafe.As<byte, T>(ref bytes[0]); // note: no out-of-range check here; dangerous
}

完整示例(适用于 net462):

using System;
using System.Runtime.CompilerServices;


struct Foo
{
    public int x, y;
}
class MyClass<T>
{
    public T Value = default(T);

    public unsafe MyClass(byte[] bytes)
    {
        if (bytes.Length < Unsafe.SizeOf<T>())
            throw new InvalidOperationException("not enough data");
        Value = Unsafe.As<byte, T>(ref bytes[0]);
    }
}
static class P
{
    static void Main() {
        byte[] bytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
        var obj = new MyClass<Foo>(bytes);
        var val = obj.Value;
        Console.WriteLine(val.x); // 67305985 = 0x04030201
        Console.WriteLine(val.y); // 134678021 = 0x08070605 
    }
}