-1

I'm trying to convert an object with only int fields (ushort, ulong, uint, int, etc.) into a byte array containing each int as a byte in the order that it appears in the object. For example, if I have an object of the form

obj = {subobj: {uint firstProp: 500, ushort secondProp: 12}, byte lastProp: 5}

then I expect the byte array to be

{0, 0, 1, 244, 0, 12, 5}

I tried to create this byte array by using Serialization (as described in this answer), but I'm noticing there's a bunch of stuff before and after each byte. Based on this website, I believe this represents the database and the file, which I don't want. I know that in C++ I can use reinterpret_cast<uint8_t*>(obj) to get the desired result. Is there an equivalent way to do this in C#?

John James
  • 219
  • 1
  • 8
  • 1
    Unfortunately `Buffer.BlockCopy` doesn't support aggregate types, even if contain only primitive members and ought to be blittable. If you're willing to compile with `/unsafe` you can use `Marshal.PtrToStructure` and its inverse. – Ben Voigt Jun 22 '21 at 21:06
  • 1
    I'd strongly recommend you don't do this as the underlying representation can change on different architectures. You can look at https://stackoverflow.com/questions/3278827/how-to-convert-a-structure-to-a-byte-array-in-c and https://learn.microsoft.com/en-us/dotnet/standard/native-interop/customize-struct-marshaling for info on the unsafe marshalling features, but you will need to compile with /unsafe as @BenVoigt says. Those features only work with structs. The better approach would be to write your own serializer method for this type that takes a BinaryWriter as a parameter. – Steve Pick Jun 22 '21 at 21:13
  • 2
    @StevePick: The underlying representation is quite well-defined, and p/invoke already relies on it. You can use `StructLayout.Explicit` and `FieldOffsetAttribute` to take full control if you aren't comfortable with the defaults. – Ben Voigt Jun 22 '21 at 21:17
  • @BenVoigt oh cool, I suppose there aren't any architectures or implementations of the compiler/runtime where it's different, then? I linked the doc on customising struct layout to show that these things can be done if absolutely necessary, but I still prefer the safe route. It is a lot of annoying boilerplate to write, though. – Steve Pick Jun 22 '21 at 21:30
  • @StevePick: The default is designed to be able to match a type defined in C, and vary platform-to-platform in exactly the same way the C structure will. Naturally any `IntPtr` member will change size between platforms, other members are the same size everywhere but may change alignment (and thus internal padding). But if your goal is interoperability between platforms rather than with native C code, simply setting `Pack=1` on the StructLayoutAttribute will disable platform-specific alignment/padding. – Ben Voigt Jun 22 '21 at 22:17

1 Answers1

0

You can try do something like this:

foreach(int value in obj)
{
    byte lsbOfLsb = (byte)value;
    byte msbOfLsb = (byte)(value >> 8);
    byte lsbOfMsb = (byte)(value >> 16);
    byte msbOfMsb = (byte)(value >> 24);
}

Obviously this is only the idea. You should use a for loop instead of a foreach and Parse all elements to int e.g. . Other way is to check the type of data with

typeof(value) //op 1
// OR
if(value is int) //e.g. 

and then convert to byte as you need.