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.