Скопируйте массив байтов в общий тип без бокса - PullRequest
0 голосов
/ 05 февраля 2019

Я разрабатываю класс C #, в котором мне нужно иметь возможность принимать байтовый массив и копировать его в универсальную переменную того же размера.В C / C ++ такая вещь (копирование) была бы легкой, но в C # не так уж и много.

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

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

Я бы предпочел не использовать бокс.Есть ли способ сделать это, используя маршалинг, отражение или неуправляемый / небезопасный код?


Я нашел этот другой пост , но единственный предложенный ответ не сработает потому что использует бокс .

1 Ответ

0 голосов
/ 05 февраля 2019

Если вы используете последнюю версию .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];
    }
}

Вы также можете использовать unsafe в последних версиях C #(для ограничения 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!");

или вы можете использовать sizeof(T), если у вас естьT : unmanaged ограничение.Вам не нужно это с решением 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 
    }
}
...