3

I'm developing a C# class wherein I need to be able to take in a byte array and copy it to a generic variable of the same size. In C/C++ such a thing (the copying) would be easy, but in C# not so much.

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

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

I'd prefer not to use boxing. Is there a way to do this using marshaling, reflection, or unmanaged/unsafe code?


I did find this other post, but the only suggested answer won't work because it uses boxing.

13
  • 1
    Do you control the process that creates the byte array? What's in the byte array? Is T constrained in any way? Are you simply looking for (de)serialization? Commented Feb 5, 2019 at 15:17
  • 1
    Whenever you write "doesn´t work for me" you should describe precisely what exactly does not work? Commented Feb 5, 2019 at 15:18
  • 1
    Just a side note: When an object is created with new, all fields are initialized to their default value automatically. You can drop the = default(T). Commented Feb 5, 2019 at 15:18
  • 1
    I think you're misunderstanding the use of generics. Generics aren't meant to stay generic, your code needs to provide a type. Maybe you want to store the bytes array in an object type or dynamic type? Commented Feb 5, 2019 at 15:20
  • 1
    @elgonzo each type you mention uses more than one byte for storage. Commented Feb 5, 2019 at 15:25

1 Answer 1

6

If you're using up-to-date .NET, you can use Span<T> (System.Buffers) for this:

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

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

You can also use unsafe in recent C# versions (for the T : unmanaged constraint):

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
        }
    }
}

You can also do some things here using Unsafe.* methods (System.Runtime.CompilerServices.Unsafe); for example (note no constraints):

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 you want to check the out-of-range problem:

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

or you can use sizeof(T) if you have the T : unmanaged constraint. You don't need this with the Span<T> solution (the first one), because the original Cast<byte, T> will yield a span of length zero in that scenario, and the [0] will throw appropriately.


I think this should work too!

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

complete example (works on 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 
    }
}
Sign up to request clarification or add additional context in comments.

9 Comments

FYI: Whether this works as intended depends on the order/endianess of the bytes in byte array. So far it is not clear where OP gets the bytes from and in which order/endianess they are stored in the byte array.
@elgonzo oh, absolutely; frankly, any serializer (even perhaps BinaryFormatter on a good day) is preferable to brute-forcing bytes into structs
@MarcGravell My application is using .NET 4.6.2. I've added using System.Runtime.InteropServices, but MemoryMarshal isn't recognized. Is that what's you'd expect? I'll try your other suggestions as well.
@Jim nuget.org/packages/System.Runtime.CompilerServices.Unsafe goes down to 4.5.2 - it might not have all the methods, though... and I doubt you can use the Span<T> version; give me a sec, I'll check
@JimFell you might as well get familiar with nuget - most of the interesting Microsoft libraries now ship on nuget, as it allows more frequent updates - plus of course the huge range of open source libraries there
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.