Say I have a collection of bytes
var bytes = new byte[] {0, 1, 2, 3, 4, 5, 6, 7};
and I want to pull out a defined value from the bytes as a managed type, e.g. a ushort
. What is a simple way to define what types reside at what location in the collection and pull out those values?
One (ugly) way is to use System.BitConverter
and a Queue
or byte[]
with an index and simply iterate through, e.g.:
int index = 0;
ushort first = System.BitConverter.ToUint16(bytes, index);
index += 2; // size of a ushort
int second = System.BitConverter.ToInt32(bytes, index);
index += 4;
...
This method gets very, very tedious when you deal with a lot of these structures!
I know that there is the System.Runtime.InteropServices.StructLayoutAttribute
which allows me to define the locations of types inside a struct or class, but there doesn't seem to be a way to import the collection of bytes into that struct. If I could somehow overlay the struct on the collection of bytes and pull out the values, that would be ideal. E.g.
Foo foo = (Foo)bytes; // doesn't work because I'd need to implement the implicit operator
ushort first = foo.first;
int second = foo.second;
...
[StructLayout(LayoutKind.Explicit, Size=FOO_SIZE)]
public struct Foo {
[FieldOffset(0)] public ushort first;
[FieldOffset(2)] public int second;
}
Any thoughts on how to achieve this?
[EDIT: See also my question on how to deal with the bytes when they are big endian.]
We have done this quite a bit as we talk directly to hardware via bytes over serial.
Given the struct definition
[StructLayout(LayoutKind.Explicit, Size=FOO_SIZE)]
public struct Foo {
[FieldOffset(0)] public ushort first;
[FieldOffset(2)] public int second;
}
You can use a class like this to perform the conversion
public class ByteArrayToStruct<StructType>
{
public StructType ConvertToStruct(int size, byte[] thebuffer)
{
try
{
int theSize = size;
IntPtr ptr1 = Marshal.AllocHGlobal(theSize);
Marshal.Copy(thebuffer, 0, ptr1, theSize);
StructType theStruct = (StructType)Marshal.PtrToStructure(ptr1, typeof(StructType));
Marshal.FreeHGlobal(ptr1);
return theStruct;
}
catch (Exception)
{
return default(StructType);
}
}
}
Conversely, you could also create List from the array and do something like the following:
ushort first = BitConverter.ToInt16(myList.ToArray(), 0);
myList.RemoveRange(0, sizeof(ushort));
[...]
This would essentially be keeping the relevant data at the "head" of the list, so you wont have to keep track of the position in the array.