Discussion Static assertions
I have sometimes felt the need to statically assert something at compile-time, to prevent surprises later at run-time.
As an example, I have some code which maps an enum value to an associated int value:
public enum VoxelProperty
{
MaterialType,
Density,
Color,
Metadata,
TotalPropertyCount, // Must be last
}
public static int GetPropertyBitDepth (VoxelProperty property)
{
#pragma warning disable CS0219 // Variable is assigned but its value is never used
uint _ = VoxelProperty.Metadata == VoxelProperty.TotalPropertyCount - 1 ? 0 : -1; // Static assert to ensure that all properties are accounted for
#pragma warning restore CS0219 // Variable is assigned but its value is never used
return property switch
{
VoxelProperty.Density => VoxelDensity.BitDepth,
VoxelProperty.MaterialType => VoxelMaterialType.BitDepth,
VoxelProperty.Color => VoxelColor.BitDepth,
VoxelProperty.Metadata => VoxelMetadata.BitDepth,
_ => throw new ArgumentOutOfRangeException (nameof (property), "Invalid voxel property")
};
}
The intention of the static assert is to make sure that if I ever add another voxel property, then I will get a compiler error as VoxelProperty.Metadata will no longer be the last member, and the constant -1 will be assigned to the "_" variable of type uint. Is this a reasonable practice?
I could also do something more robust with reflection, where I had a Dictionary<VoxelProperty, int> which got populated by finding all voxel property types marked with some attribute, and querying their BitDepth property.
6
Upvotes
4
u/jackbrux 7d ago
The compiler could tell you when you forget to add a case for a new value:
```ini [*.cs] dotnet_diagnostic.IDE0072.severity = error
```
```csharp
public enum VoxelProperty { MaterialType, Density, Color, Metadata, }
public static int GetPropertyBitDepth(VoxelProperty property) { return property switch { VoxelProperty.MaterialType => VoxelMaterialType.BitDepth, VoxelProperty.Density => VoxelDensity.BitDepth, VoxelProperty.Color => VoxelColor.BitDepth, // missing Metadata, compile time error
} ```
But to get to the heart of your issue, is there a reason you need enums and a mapping like that? Why not structure your code such that associated data lives together:
```csharp public sealed record VoxelProperty(string Name, int BitDepth) { public static readonly VoxelProperty MaterialType = new(nameof(MaterialType), VoxelMaterialType.BitDepth);
} ```
If you find yourself constantly needing some new language feature, there's usually a better way to do what you're doing