10

I want to do the equivalent of this:

byte[] byteArray; enum commands : byte {one, two}; commands content = one; byteArray = (byte*)&content; 

yes, it's a byte now, but consider I want to change it in the future? how do I make byteArray contain content? (I don't care to copy it).

5 Answers 5

21

To convert any value types (not just primitive types) to byte arrays and vice versa:

 public T FromByteArray<T>(byte[] rawValue) { GCHandle handle = GCHandle.Alloc(rawValue, GCHandleType.Pinned); T structure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); handle.Free(); return structure; } public byte[] ToByteArray(object value, int maxLength) { int rawsize = Marshal.SizeOf(value); byte[] rawdata = new byte[rawsize]; GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned); Marshal.StructureToPtr(value, handle.AddrOfPinnedObject(), false); handle.Free(); if (maxLength < rawdata.Length) { byte[] temp = new byte[maxLength]; Array.Copy(rawdata, temp, maxLength); return temp; } else { return rawdata; } } 
Sign up to request clarification or add additional context in comments.

6 Comments

Very nice, exactly what i've been searching for, this should be the correct answer as BitConverter doesn't work for all types
BitConverter is consistent across all supported types on all platforms where it is implemented though. It should be noted that the bytes you get back from this method are NOT portable across different platforms, and may not even work across different versions of the .NET framework, so this isn't suitable for serialization unless you can guarantee the .NET framework version is identical on the serializing and deserializing end. Could make upgrading your application pretty difficult.
@MikeMarynowski: Just saw your comment. I don't know what you mean. This code does NOT involve binary serialization, it uses marshalling, and the point of marshalling is to get exactly the bytes you're requesting to call P/Invoke or COM APIs. With this method, you get exactly the bytes you defined in your struct (with StructLayoutAttribute and the like), no matter the .NET version or the platform.
@OregonGhost, Sleek solution, but I'm wondering if a structure is not defined with StructLayoutAttributes, what decides the order of output bytes? Cannot it change from version to version of .NET? Maybe this is what Mike means.
@CRG: Maybe. But I'd never even consider using marshalling with structs that are not properly declared with StructLayoutAttribute. With the basic structs (i.e. the types that are supported by BitConverter anyway), this will also never change. BitConverter is not consistent across platforms anyway, it depends on the system's endianness, just like marshalling. That's why you typically add properties for integer fields that handle endianness, with the actual fields being already in network order.
|
17

The BitConverter class might be what you are looking for. Example:

int input = 123; byte[] output = BitConverter.GetBytes(input); 

If your enum was known to be an Int32 derived type, you could simply cast its values first:

BitConverter.GetBytes((int)commands.one); 

1 Comment

If primitive types are not enough, see my answer for how to convert any value type.
5

For anyone who's interested in how it works without using BitConverter you can do it like this:

// Convert double to byte[] public unsafe byte[] pack(double d) { byte[] packed = new byte[8]; // There are 8 bytes in a double void* ptr = &d; // Get a reference to the memory containing the double for (int i = 0; i < 8; i++) { // Each one of the 8 bytes needs to be added to the byte array packed[i] = (byte)(*(UInt64 *)ptr >> (8 * i)); // Bit shift so that each chunk of 8 bits (1 byte) is cast as a byte and added to array } return packed; } // Convert byte[] to double public unsafe double unpackDouble(byte[] data) { double unpacked = 0.0; // Prepare a chunk of memory ready for the double void* ptr = &unpacked; // Reference the double memory for (int i = 0; i < data.Length; i++) { *(UInt64 *)ptr |= ((UInt64)data[i] << (8 * i)); // Get the bits into the right place and OR into the double } return unpacked; } 

In reality it's much easier and safer to use BitConverter but it's fun to know!

1 Comment

This kind of approach is starting to be more relevant again with the addition of Spans when trying to write low allocation code... Exploring this myself and wondering if there;s anything wrong with doing the write via ((*tvalue)bytePointer)[0] = value (assuming the size has already been checked, of course)
3

You can use the BitConverter.GetBytes method to do this.

Comments

1

Here is a generic and unsafe variant if you care about performance and don't want to mess with GC

 static unsafe void StructToByteArr<T>(ref T obj, Span<byte> dest) where T : unmanaged { fixed (void* pSrc = &obj, pDest = dest) { Buffer.MemoryCopy(pSrc, pDest, dest.Length, sizeof(T)); } } static unsafe void StructToByteArr<T>(ref T obj, byte[] dest, int destOffset) where T : unmanaged { StructToByteArr(ref obj, new Span<byte>(dest, destOffset, sizeof(T))); } static unsafe byte[] StructToByteArr<T>(ref T obj) where T : unmanaged { var result = new byte[sizeof(T)]; StructToByteArr(ref obj, result, 0); return result; } 

You can remove ref if you're passing a struct smaller than the size of a pointer.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.