Table of Contents

Read into a C# class

Beginner · C#. The complete program includes the Point class, whose ReadFrom names the layout members it reads and whose module initializer registers it; no reflection is involved.

Run this example

Prerequisites: the repository's .NET 10 SDK and a checkout of this source. Run from the repository root:

dotnet run --project docs/examples/CStructSharp.Docs.Examples.csproj -c Release -- map-poco

The runner checks Point with X -2 and Y 5. Success includes PASS map-poco.

This example uses the C# API. Browser capabilities and result shapes are described in the browser guide.

Complete program

The layout, options, input bytes, helper methods, and required types are all included. To adapt it outside the repository, create a .NET 10 console project, add CStructSharp, and replace Program.cs with this complete file. These examples follow the source version; use a matching package when testing a release.

Download the complete C# source.

// Generated from executable documentation examples. Edit the source region, then regenerate.
using System;
using System.IO;
using System.IO.Pipelines;
using System.Linq;
using System.Buffers;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using CStructSharp;
using CStructSharp.Codecs;
using CStructSharp.Diagnostics;
using CStructSharp.Introspection;
using CStructSharp.Values;

internal static partial class Program
{
    public static void Main()
    {
        MapPoco();
        Console.WriteLine("PASS map-poco");
    }

    private static void MapPoco()
    {
        var layout = new CStruct("struct point { int16 x; int16 y; };");
        Point point = layout.ReadValue<Point>(new byte[] { 0xFE, 0xFF, 0x05, 0x00 }, "point");
        Equal((short)-2, point.X);
        Equal((short)5, point.Y);
    }

    private static void Equal<T>(T expected, T actual)
    {
        if (!EqualityComparer<T>.Default.Equals(expected, actual))
        {
            throw new InvalidOperationException($"Expected '{expected}', received '{actual}'.");
        }
    }

    public sealed class Point : ICStructMapped<Point>
    {
        public short X { get; set; }

        public short Y { get; set; }

        // The mapper names the layout members it reads; Get<T> converts each one with range checks.
        public static Point ReadFrom(StructValue source)
        {
            return new Point { X = source.Get<short>("x"), Y = source.Get<short>("y") };
        }

        public static void WriteTo(Point value, StructValue target)
        {
            target["x"] = value.X;
            target["y"] = value.Y;
        }

        // Runs before any other code in the assembly; the [CStructMapped] generator emits the same registration.
        [ModuleInitializer]
        internal static void Register()
        {
            MappedTypes.Register<Point>();
        }
    }
}

Try it and diagnose mistakes

Change FE FF to FF FF.

Answer: X becomes -1, not 65535, because int16 is signed. The program contains assertions for its original inputs. When changing an input intentionally, update the expected assertion too; an unchanged assertion is not evidence that the new value is wrong.

Continue with the related guide or choose another recipe.