Search code examples
vb.netbitwise-operatorsbitwise-andbitwise-xor

Bitwise operations - checking and removal


Note the simple example below:

Module Module1
    <Flags>
    Public Enum Names
        None    = 0
        Test    = 1
        Test2   = 2
        Test3   = 4
        Test4   = 8
    End Enum

    Sub Main()
        Dim test As Names = Names.Test Or Names.Test3
        If (test And Names.Test3) = Names.Test3
            Console.WriteLine("TRUE")
        Else
            Console.WriteLine("FALSE")
        End If
    End Sub
End Module

The first part of my question relates to the line If (test And Names.Test3) = Names.Test3.

Wouldn't it be better to simply check If test And Names.Test3 as if the flag exists? If it evaluates to a non-zero value (meaning the flag exists) then the result of the condition would be True anyway.

Is there a good reason to use the first way of checking over the second? (Whilst my answer is for VB.NET, I would also be interested in knowing if this is a potential pitfall anywhere else, ie C#, C++, etc).

Also, with regards to flag removal, it seems there's two ways to do this:

test = test Xor Names.Test3 and test = test And Not Names.Test3

However, the first will add the flag if it's missing, and remove it if it is there, whereas the second will only remove it. Is that the only difference? Or is there another reason why I should prefer one method over the other?


Solution

  • You are correct in stating that you can effectively replace this:

    If (test And Names.Test3) = Names.Test3 Then

    with this

    If (test And Names.Test3) Then

    But, the second example will not compile with Option Strict On as you rightly get the error:

    Option Strict On disallows implicit conversions from 'Names' to 'Boolean' so in order to get this to compile you need to wrap a CBool round it.

    So, in conclusion I would say it is much better to use the first example as the intent is very clear:- you are checking to see if a bit is set.

    In terms of flag removal i.e. unsetting a bit you should use:

    test = test And Not Names.Test3

    Using Xor has the effect of toggling the value.

    The following might help (especially if you make them extension methods):

    Public Function SetBit(ByVal aValue As Names, ByVal aBit As Names) As Names
        Return (aValue Or aBit)
    End Function
    
    Public Function ClearBit(ByVal aValue As Names, ByVal aBit As Names) As Names
        Return (aValue And Not aBit)
    End Function
    
    Public Function IsBitSet(ByVal aValue As Names, ByVal aBit As Names) As Boolean
        Return ((aValue And aBit) = aBit)
    End Function
    
    Public Function ToggleBit(ByVal aValue As Names, ByVal aBit As Names) As Names
        Return (aValue Xor aBit)
    End Function