ReadOnlyExtendedBitFieldAttribute

ReadOnlyExtendedBitFieldAttribute combines the functionality of ReadOnlyBitFieldAttribute and ExtendedBitFieldAttribute. It provides bit-packing for read-only properties while allowing values larger than the allocated bit size by redirecting to an external getter.

What is ReadOnlyExtendedBitFieldAttribute?

Inherits from both ReadOnlyBitFieldAttribute and ExtendedBitFieldAttribute
Supports all standard bit-packing features from BitFieldAttribute
Automatically generates constructors for read-only properties
Allows properties to store values larger than allocated bits
Uses an external getter when values exceed the bit limit
Requires GetterLargeSizeValueName for overflow handling
Supports FieldName for referencing existing read-only fields


Example: Read-Only Bit-Packed Properties with Large Values

public sealed partial class ReadOnlyExtendedBitFieldExample
{
    private readonly int _existingField;

    [ReadOnlyExtendedBitField(BitsCount = 5, GetterLargeSizeValueName = nameof(GetMaxValue))]
    public partial int Data1 { get; }

    [ReadOnlyExtendedBitField(BitsCount = 10, GetterLargeSizeValueName = nameof(GetMaxValue), ConstructorAccessModifier = AccessModifier.Public)]
    public partial int Data2 { get; }

    [ReadOnlyExtendedBitField(BitsCount = 6, FieldName = nameof(_existingField), GetterLargeSizeValueName = nameof(GetMaxValue), ConstructorAccessModifier = AccessModifier.Public)]
    public partial int Data3 { get; }

    public static int GetMaxValue() => 1024; // External getter for overflow values
}

How It Works

1️⃣ BitsCount allocates bits for storage, but if the value exceeds the max, the getter calls GetterLargeSizeValueName.
2️⃣ ReadOnlyExtendedBitFieldAttribute ensures immutability by allowing only read-only properties.
3️⃣ Constructors are automatically generated, grouping properties with the same ConstructorAccessModifier.
4️⃣ FieldName allows referencing existing fields, but they must be readonly.


Generated Code

1️⃣ Constructor for Properties with Private Access (Default)

partial class ReadOnlyExtendedBitFieldExample
{
    private ReadOnlyExtendedBitFieldExample(int Data1)
    {
        {
            const ushort maxData1_ = (1 << 5) - 1;
            var clampedData1_ = global::System.Math.Min((ushort)(Data1), maxData1_);
            this._Data1__Data2__ = (ushort)((this._Data1__Data2__ & ~(((1 << 5) - 1) << 0)) | ((clampedData1_ & ((1 << 5) - 1)) << 0));
        }
    }
}

2️⃣ Public Constructor for Properties with Public Access

partial class ReadOnlyExtendedBitFieldExample
{
    public ReadOnlyExtendedBitFieldExample(int Data3, int Data2)
    {
        {
            const int maxData3_ = (1 << 6) - 1;
            var clampedData3_ = global::System.Math.Min((int)(Data3), maxData3_);
            this._existingField = (int)((this._existingField & ~(((1 << 6) - 1) << 0)) | ((clampedData3_ & ((1 << 6) - 1)) << 0));
        }

        {
            const ushort maxData2_ = (1 << 10) - 1;
            var clampedData2_ = global::System.Math.Min((ushort)(Data2), maxData2_);
            this._Data1__Data2__ = (ushort)((this._Data1__Data2__ & ~(((1 << 10) - 1) << 5)) | ((clampedData2_ & ((1 << 10) - 1)) << 5));
        }
    }
}

Common Errors

1️⃣ Referencing a Non-Readonly Field

If FieldName references a non-readonly field, an error occurs:

PRBITS013
Invalid reference to non-readonly field in 'ReadOnlyBitFieldAttribute.FieldName'.
The 'FieldName' for property {0} must reference a readonly field when using the nameof operation.

Fix: Ensure the field is readonly.

private readonly int _bitField; // ✅ Allowed

[ReadOnlyExtendedBitField(FieldName = nameof(_bitField), BitsCount = 8, GetterLargeSizeValueName = nameof(GetMaxValue))]
public partial int Data { get; }

🚫 Invalid Code (Causes Error)

private int _bitField; // ❌ Not readonly

[ReadOnlyExtendedBitField(FieldName = nameof(_bitField), BitsCount = 8, GetterLargeSizeValueName = nameof(GetMaxValue))]
public partial int Data { get; } // ❌ Compiler error: PRBITS013

2️⃣ Property Must Be Read-Only

If a property has a setter, it must be init-only or read-only. Otherwise, an error occurs:

PRBITS014
ReadOnlyBitFieldAttribute requires property without setter or with init-only setter.
The property {0} with ReadOnlyBitFieldAttribute must either be read-only or have an init-only setter.

🚫 Invalid Code (Causes Error)

[ReadOnlyExtendedBitField(BitsCount = 6, GetterLargeSizeValueName = nameof(GetMaxValue))]
public partial int Value { get; set; } // ❌ Compiler error: PRBITS014

Valid Code

[ReadOnlyExtendedBitField(BitsCount = 6, GetterLargeSizeValueName = nameof(GetMaxValue))]
public partial int Value { get; } // ✅ Read-only property

[ReadOnlyExtendedBitField(BitsCount = 6, GetterLargeSizeValueName = nameof(GetMaxValue))]
public partial int Value2 { get; init; } // ✅ Init-only setter

Why Use ReadOnlyExtendedBitFieldAttribute?

Ensures immutability – Values set only through constructor or external getter
Optimized for large values – Handles numbers beyond allocated bits
Automatically generates constructors – Groups properties with same ConstructorAccessModifier
Uses FieldName to pack into existing fields – But they must be readonly
Defaults to private constructor unless explicitly changed


Important Notes

ReadOnlyExtendedBitFieldAttribute inherits from both ReadOnlyBitFieldAttribute and ExtendedBitFieldAttribute, meaning it supports all features of both.
⚠ The generator automatically creates constructors based on ConstructorAccessModifier.
If a property exceeds its bit size, the getter redirects to GetterLargeSizeValueName.
By default, the constructor is private, unless ConstructorAccessModifier is specified.