.NET Fiddle

CSharp · Console

/Program.cs

using System;
using System.Linq;
using System.Runtime.InteropServices;

namespace Demo
{
    static class Program
    {
        static void Main()
        {
            var demo1 = new Demo1
            {
                BoolValue   = true,
                DoubleValue = -1
            };

            var demo2 = new Demo2
            {
                CharValue   = '0',
                ShortValue  = 1,
                IntValue    = 2,
                LongValue   = 3,
                FloatValue  = 4,
                DoubleValue = 5.4321
            };

            var demo3 = new Demo3
            {
                ByteValue  = 128,
                FloatValue = 1.23f
            };

            const int PREFIX_BYTE_COUNT  = 29;
            const int POSTFIX_BYTE_COUNT = 17;

            var bytes = toByteArray(PREFIX_BYTE_COUNT, POSTFIX_BYTE_COUNT, ref demo1, ref demo2, ref demo3);

            consumeStructs(PREFIX_BYTE_COUNT, bytes);
        }

        static byte[] toByteArray(int prefixByteCount, int postfixByteCount, ref Demo1 demo1, ref Demo2 demo2, ref Demo3 demo3)
        {
            var demo1Bytes = AsSpan(ref demo1);
            var demo2Bytes = AsSpan(ref demo2);
            var demo3Bytes = AsSpan(ref demo3);

            var prefixBytes  = Enumerable.Repeat((byte)0, prefixByteCount);
            var postfixBytes = Enumerable.Repeat((byte)0, postfixByteCount);

            return prefixBytes
               .Concat(demo1Bytes.ToArray())
               .Concat(demo2Bytes.ToArray())
               .Concat(demo3Bytes.ToArray())
               .Concat(postfixBytes)
               .ToArray();
        }

        static void consumeStructs(int prefixByteCount, byte[] data)
        {
            var demo1Bytes = new Span<byte>(data, prefixByteCount,                                                     Marshal.SizeOf<Demo1>());
            var demo2Bytes = new Span<byte>(data, prefixByteCount + Marshal.SizeOf<Demo1>(),                           Marshal.SizeOf<Demo2>());
            var demo3Bytes = new Span<byte>(data, prefixByteCount + Marshal.SizeOf<Demo1>() + Marshal.SizeOf<Demo2>(), Marshal.SizeOf<Demo3>());

            ref var demo1Ref = ref MemoryMarshal.AsRef<Demo1>(demo1Bytes);
            ref var demo2Ref = ref MemoryMarshal.AsRef<Demo2>(demo2Bytes);
            ref var demo3Ref = ref MemoryMarshal.AsRef<Demo3>(demo3Bytes);

            Console.WriteLine(demo1Ref);
            Console.WriteLine(demo2Ref);
            Console.WriteLine(demo3Ref);

            Console.WriteLine("Modifying first byte of Demo2 struct in byte buffer.");

            data[prefixByteCount + Marshal.SizeOf<Demo1>()] = (byte)'*';
            Console.WriteLine(demo2Ref);
        }

        public static Span<byte> AsSpan<T>(ref T val) where T : unmanaged
        {
            var valSpan = MemoryMarshal.CreateSpan(ref val, 1);
            return MemoryMarshal.Cast<T, byte>(valSpan);
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 8)]
    public struct Demo1
    {
        public bool   BoolValue;
        public double DoubleValue;

        public override string ToString()
        {
            return $"byte = {BoolValue}, double = {DoubleValue}";
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
    public struct Demo2
    {
        public char   CharValue;
        public short  ShortValue;
        public int    IntValue;
        public long   LongValue;
        public float  FloatValue;
        public double DoubleValue;

        public override string ToString()
        {
            return $"char = {CharValue}, short = {ShortValue}, int = {IntValue}, long = {LongValue}, float = {FloatValue}, double = {DoubleValue}";
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 2)]
    public struct Demo3
    {
        public byte  ByteValue;
        public float FloatValue;

        public override string ToString()
        {
            return $"byte = {ByteValue}, float = {FloatValue}";
        }
    }
}
;