C#idiomatic C#
how-to-create-a-c-cpp-union-by-using-attributescsharp/advanced-topics/reflection-and-attributes

Advanced

How to create a C/C++ union by using attributes in C#

Learn how to use attributes to customize how structs are laid out in memory in C#. This example implements the equivalent of a union from C/C++.

  • page csharp/advanced-topics/reflection-and-attributes/how-to-create-a-c-cpp-union-by-using-attributes
  • updated 03/15/2023

By using attributes, you can customize how structs are laid out in memory. For example, you can create what is known as a union in C/C++ by using the StructLayout(LayoutKind.Explicit) and FieldOffset attributes.

In this code segment, all of the fields of TestUnion start at the same location in memory.

sample · csharpdocs/csharp/advanced-topics/reflection-and-attributes/snippets/conceptual/CStyleUnion.csSameLocation

csharp
using System.Runtime.InteropServices;

namespace AttributesAndReflection;

// <SameLocation>
[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
struct TestUnion
{
    [System.Runtime.InteropServices.FieldOffset(0)]
    public int i;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public double d;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public char c;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public byte b;
}
// </SameLocation>

// <ExplicitLayout>
[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
struct TestExplicit
{
    [System.Runtime.InteropServices.FieldOffset(0)]
    public long lg;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public int i1;

    [System.Runtime.InteropServices.FieldOffset(4)]
    public int i2;

    [System.Runtime.InteropServices.FieldOffset(8)]
    public double d;

    [System.Runtime.InteropServices.FieldOffset(12)]
    public char c;

    [System.Runtime.InteropServices.FieldOffset(14)]
    public byte b;
}
// </ExplicitLayout>

The following code is another example where fields start at different explicitly set locations.

sample · csharpdocs/csharp/advanced-topics/reflection-and-attributes/snippets/conceptual/CStyleUnion.csExplicitLayout

csharp
using System.Runtime.InteropServices;

namespace AttributesAndReflection;

// <SameLocation>
[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
struct TestUnion
{
    [System.Runtime.InteropServices.FieldOffset(0)]
    public int i;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public double d;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public char c;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public byte b;
}
// </SameLocation>

// <ExplicitLayout>
[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
struct TestExplicit
{
    [System.Runtime.InteropServices.FieldOffset(0)]
    public long lg;

    [System.Runtime.InteropServices.FieldOffset(0)]
    public int i1;

    [System.Runtime.InteropServices.FieldOffset(4)]
    public int i2;

    [System.Runtime.InteropServices.FieldOffset(8)]
    public double d;

    [System.Runtime.InteropServices.FieldOffset(12)]
    public char c;

    [System.Runtime.InteropServices.FieldOffset(14)]
    public byte b;
}
// </ExplicitLayout>

The two integer fields, i1 and i2 combined, share the same memory locations as lg. Either lg uses the first 8 bytes, or i1 uses the first 4 bytes and i2 uses the next 4 bytes. This sort of control over struct layout is useful when using platform invocation.

See also