Reputation: 1369
I've seen a number of methods to copy a byte[] to a struct, and vise-versa. However, I was wondering if it was possible to cast the byte[] pointer to a struct (like you do in C)..
I want to be able to cast a byte[] to a struct, make changes to the struct, and have the changes automatically appear in the byte[].
Thanks, reza
Upvotes: 2
Views: 324
Reputation: 81207
I would suggest that your best bet, if speed isn't critical, would probably be to have the data stored in a byte[]
, and have a class which holds an immutable reference to that byte[]
and has properties whose get/set methods access the array. Property gets/sets would always reflect and be reflected in the state of the array, since the array itself would hold the state of the object. No "unsafe" code required.
Methods would probably look something like:
public static class IntPack
{ // All methods ssume unchecked arithmetic
public static Int16 FetchI16LE(this byte[] dat, int offset)
{
return (Int16)(dat[offset] + (dat[offset + 1] << 8));
}
public static Int32 FetchI32LE(this byte[] dat, int offset)
{
return dat[offset] + (dat[offset + 1] << 8) +
(dat[offset + 2] << 16) + (dat[offset + 3] << 24);
}
public static void StuffI16LE(this byte[] dat, int offset, int value)
{
dat[offset] = (byte)(value);
dat[offset+1] = (byte)(value >> 8);
}
public static void StuffI32LE(this byte[] dat, int offset, int value)
{
dat[offset] = (byte)(value);
dat[offset + 1] = (byte)(value >> 8);
dat[offset + 2] = (byte)(value >> 16);
dat[offset + 3] = (byte)(value >> 24);
}
}
The methods indicated assume little-endian ordering. One could easily write corresponding __BE methods methods for big-endian.
Upvotes: 0
Reputation: 1369
I found this approach was the simplest to achieve what I wanted.. If you define the struct so that the byte[] overlaps with the elements, copies are effectively transparent between the struct and byte[] (assuming the endian-ness is what you expect; in my case, it is).
[StructLayout(LayoutKind.Explicit)]
public unsafe struct ListEntry {
[System.Runtime.InteropServices.FieldOffset(0)] public fixed byte raw[512];
[System.Runtime.InteropServices.FieldOffset(0)] public byte version;
[System.Runtime.InteropServices.FieldOffset(1)] public UInt16 magic;
[System.Runtime.InteropServices.FieldOffset(3)] public UInt32 start_time;
[System.Runtime.InteropServices.FieldOffset(7)] public UInt16 run_id;
[System.Runtime.InteropServices.FieldOffset(9)] public UInt16 channels;
[System.Runtime.InteropServices.FieldOffset(11)] public UInt16 sampling_rate;
[System.Runtime.InteropServices.FieldOffset(13)] public UInt32 start_sector;
[System.Runtime.InteropServices.FieldOffset(510)] public UInt16 checksum;
}
Upvotes: 0
Reputation: 1063328
You just cast the pointer (sometimes you need to go via void*
in the middle):
struct Foo
{
public int Bar;
}
static unsafe void Main()
{
byte[] buffer = new byte[10];
fixed (byte* untyped = buffer)
{
var typed = (Foo*)untyped;
typed[0].Bar = 123;
}
// buffer has the changes
}
If you need to offset into the buffer, then use byte* untyped = &buffer[offset]
.
If you want a raw struct pointer, then:
fixed (byte* ptr = buffer)
{
var typed = (Foo*)ptr;
Foo* foo = &typed[0];
foo->Bar = 123;
}
However, note that you can't pass a Foo*
to methods expecting a Foo
or ref Foo
.
Upvotes: 1