Skip to content

Default property for reading and setting bit flags #228

@AnthonyDGreen

Description

@AnthonyDGreen

Bit twiddling to check and set flags is arcane. Given a flag enumeration of the form:

<Flags>
Enum Color
    Red = 1
    Green = 2
    Blue = 4
End Enum

To check whether a flag (or set of flags) is set you AND the bit mask with the value and compare it to itself:

Dim isRed = ((value And Color.Red) = Color.Red)
Dim isYellow = (value And (Color.Red Or Color.Green)) = (Color.Red Or Color.Green))

To set a flag on you OR the value with the mask:

value = (value Or Color.Red)

To set it to off you AND it with the negation of the flag (I think):

value = (value And Not Color.Red)

It also has this unfortunate side-effect that to see if both Red and Green are set you have to Or them. None of this is intuitive, fun, or memorable, and it lacks VB's brand of straightforward approachability. I propose that instead of forcing VB users to remember this low-level operations, adding an implicit default property to all the integral numeric types and any <Flags> enumerated types which takes a mask and returns true if the specified bits are on and false if they aren't and which lets you set the bits in the supplied mask to on or off.

To check whether a flag (or set of flags) is set you just index the value with the flag:

' Explicitly comparing to True for clarity
If value(Color.Red) = True Then
    ' Red.
End If

To set a flag on you set "the bit(s)" at that mask to true:

value(Color.Red) = True

To set it to off you set the bits at that mask to false:

value(Color.Red) = False

And the neat thing is that it's concise enough that if you want to check for more than one flag you can And the individual checks instead of Oring the values:

If value(Color.Red) = True AndAlso value(Color.Green) = True Then
    ' Yellow.
ElseIf value(Color.Red) = True OrElse value(Color.Green) = True Then
    ' I don't know why one would check this.
End If

That's much better, I think. I don't have to remember anything. The System.Enum.HasFlags method was added in .NET 4.0 to make this simpler but it uses reflection, is comparatively slow, and isn't strongly-typed. One could write extension methods but you'd need to do this on a per-type basis. BitVector32 is compact and speedy but not strongly-typed and limited to 32-bit integer stores. BitArray has a variable capacity but allocates a lot. With this we can keep the size and performance characteristics of using the integral types directly with strong typing and the compiler can optimize the Or and And cases for constants so that it has the same performance as the code you would write today.

Breaking Change
This could technically be a very minor breaking change because VB today lets you define an "extension readonly default property" on any type by defining an ElementAtOrDefault extension function and a Select extension method to make it a queryable type. There is no way to emulate the case of making the default property writeable.

This entire scenario seems extremely unlikely but the breaking change could be avoided entirely by binding this way only after the queryable/ElementAtOrDefault check fails.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions