The other day, I was using the RegexOptions enum when creating a Regular Expression, and I thought it would be useful to dedicate a blog entry to using enumeration values as flags.
The following is a poor example, but it should be illustrative enough.
Imagine we have a Client class, and one of the Client properties is ClientState. The ClientState enum can be defined as follows:
public enum ClientStates
{
Ordinary,
HasDiscount,
IsSupplier,
IsBlackListed,
IsOverdrawn
}
As you can see - these options could be combined in several ways. Rather then creating separate properties in the Client class, we could enable a bitwise combination of enumeration values by using the FlagsAttribute, when defining our enum type. See below.
[Flags]
public enum ClientStates
{
Ordinary,
HasDiscount,
IsSupplier,
IsBlackListed,
IsOverdrawn
}
This means we can now use the bitwise OR operator to combine these enum values. For instance:
Client c = new Client();
c.ClientState = (ClientStates.HasDiscount|ClientStates.IsSupplier|ClientStates.IsOverdrawn);
By setting the FlagsAttribute, the values for each enum value now effectively become bitflag patterns that look as follows if you see them in binary form:
00000000 0
00000001 1
00000010 2
00000100 4
00001000 16
00010000 32
00100000 64
01000000 128
As you can see, this bit pattern in our enum values enables us to use the bitwise operator and combine enum values. It is plain to see that all enum values combined, using the bitwise OR, would result in 11111111.
So, how do we check what particular ClientState values have been set? The key is the bitwise AND operator. Let's take our existing Client object 'c'.
[Flags]
public enum ClientStates
{
Ordinary, // 0000
HasDiscount, // 0001
IsSupplier, // 0010
IsBlackListed, // 0100
IsOverdrawn // 1000
}
So for instance, if we want to check c.ClientState for ClientStates.HasDiscount we can use the following expression:
(c.ClientState & ClientStates.HasDiscount) == ClientStates.HasDiscount
Assuming that c.ClientState contains HasDiscount, IsSupplier and IsOverdrawn, the bit patterns would look like:
c.ClientState: 1011
ClientStates.HasDiscount: 0001
(Logical AND): 0001
So by AND’ing the bit value to check for with the combined bit pattern, we either get the same enum bit pattern value back, or we get a bit pattern which only contains zero’s.
Here’s a little helper method you can use to check for particular ClientStates enum flag values:
public static bool ContainsClientState(ClientStates combined, ClientStates checkagainst)
{
return ((combined & checkagainst)==checkagainst);
}
Namespace MyEvent
<Flags()> _
Public Enum EventFlags
OnEvent1 = 1
OnEvent2 = 2
OnEvent3 = 4
OnEventAll = 8
End Enum
End Namespace
Module AppMain
Sub main()
Dim myPublisher As New MyPublisher
Dim mySubscriber As New MySubscriber
myPublisher.Subscribe(mySubscriber, EventFlags.OnEvent1 Or EventFlags.OnEvent2)
myPublisher.FireEvent(EventFlags.OnEvent2)
Dim frm As New System.Windows.Forms.OpenFileDialog
End Module Public Class MyPublisher Public Event Event1 As AsyncInvoke.EventInvokeHelper.GenericEventHandler(Of Object, EventArgs) Public Sub Subscribe(ByVal subscriber As IMySubscriber, ByVal eventType As EventFlags) Public Sub FireEvent(ByVal eventType As EventFlags) End Class
Console.ReadKey()
End Sub
Public Event Event2 As AsyncInvoke.EventInvokeHelper.GenericEventHandler(Of Object, EventArgs)
Public Event Event3 As AsyncInvoke.EventInvokeHelper.GenericEventHandler(Of Object, EventArgs)
If (eventType And EventFlags.OnEvent1) = EventFlags.OnEvent1 Then
AddHandler Me.Event1, AddressOf subscriber.OnEvent1
End If
If (eventType And EventFlags.OnEvent2) = EventFlags.OnEvent2 Then
AddHandler Me.Event2, AddressOf subscriber.OnEvent2
End If
If (eventType And EventFlags.OnEvent3) = EventFlags.OnEvent3 Then
AddHandler Me.Event3, AddressOf subscriber.OnEvent3
End If
End Sub
If (eventType And EventFlags.OnEvent1) = EventFlags.OnEvent1 Then
AsyncInvoke.EventInvokeHelper.FireSync(Event1Event, Me, EventArgs.Empty)
End If
If (eventType And EventFlags.OnEvent2) = EventFlags.OnEvent2 Then
AsyncInvoke.EventInvokeHelper.FireSync(Event2Event, Me, EventArgs.Empty)
End If
If (eventType And EventFlags.OnEvent3) = EventFlags.OnEvent3 Then
AsyncInvoke.EventInvokeHelper.FireSync(Event3Event, Me, EventArgs.Empty)
End If
End Sub