## Background

Given two values of an enumeration, we can perform certain operations on them. Basically, these operations are limited to equality operations and bitwise operations. Mathematical operations (if they make sense in your application) require casting to the underlying type, performing the operation, and casting back to the enumeration type.

A problem arises when we write a generic class and we want to use an enumeration as a generic parameter. The `enum`

keyword is not allowed as a generic constraint (nor is `System.Enum`

), so inside the generic class we can't perform the operations that are usually available. `System.Enum`

implements `System.IComparable`

so we can use that as a constraint which will allow us to perform equality operations.

if ( T1.CompareTo ( T2 ) == 0 ) ...

That may be enough for many classes, but some classes that use enumerations will need to perform bitwise or mathematical operations. For these, we'll have to convert to another type and back. But which type?

The underlying type of an enumeration can be any of [ `byte`

, `sbyte`

, `short`

, `ushort`

, `int`

, `uint`

, `long`

, `ulong`

] and none of these can contain the full range of all the others, so picking one and using it in a generic class is not the best solution. Fortunately, the `System.Enum.GetUnderlyingType`

method will let us know which type should be used. The task then is to implement methods that perform each operation on each possible underlying type, and be able to select the appropriate set of methods.

This article describes my `EnumOperators`

class, which is my solution to this situation.

## EnumOperators

This is a generic class that takes an enumeration as a generic parameter. We can't constrain that parameter to `enum`

, but we can constrain it to `System.IComparable`

.

public static partial class EnumOperators<T>
where T : System.IComparable
{
}

An enumeration and delegates will help with selecting the appropriate set of methods to use.

private enum SupportedType
{
Byte
,
SByte
,
Int16
,
UInt16
,
Int32
,
UInt32
,
Int64
,
UInt64
}
public delegate T DelegateT ( T Op1 , T Op2 ) ;
public delegate bool DelegateBool ( T Op1 , T Op2 ) ;

We'll need some fields to store the underlying type and delegates.

public static readonly System.Type UnderlyingType ;
public static readonly DelegateT Add ;
public static readonly DelegateT Subtract ;
public static readonly DelegateT Multiply ;
public static readonly DelegateT Divide ;
public static readonly DelegateT Power ;
public static readonly DelegateT And ;
public static readonly DelegateT Or ;
public static readonly DelegateT Xor ;

#### The Constructor

The constructor simply checks that the generic parameter is an enumeration, then accesses the underlying type and uses it to map the correct methods.

static EnumOperators
(
)
{
System.Type basetype = typeof(T) ;
if ( !basetype.IsEnum )
{
throw ( new System.ArgumentException ( "T must be an Enum" ) ) ;
}
UnderlyingType = System.Enum.GetUnderlyingType ( basetype ) ;
switch
(
(SupportedType) System.Enum.Parse
(
typeof(SupportedType)
,
UnderlyingType.Name
)
)
{
case SupportedType.SByte :
{
Add = new Delegate ( Int8Add ) ;
Subtract = new Delegate ( Int8Subtract ) ;
Multiply = new Delegate ( Int8Multiply ) ;
Divide = new Delegate ( Int8Divide ) ;
Power = new Delegate ( Int8Power ) ;
And = new Delegate ( Int8And ) ;
Or = new Delegate ( Int8Or ) ;
Xor = new Delegate ( Int8Xor ) ;
break ;
}
}
return ;
}

#### Equality Operations

Because we constrained the generic parameter to `System.IComparable`

, we can use the `CompareTo `

method to implement the equality operations.

These methods differ only by which operator they use.

public static bool
Equal
(
T Op1
,
T Op2
)
{
return ( Op1.CompareTo ( Op2 ) == 0 ) ;
}

#### Mathematical and Bitwise Operations

These methods differ by what intermediate type and which operator they use.

(The Power methods use `System.Math.Pow`

.)

private static T
Int8Add
(
T Op1
,
T Op2
)
{
return ( (T) System.Convert.ChangeType
(
(System.SByte) System.Convert.ChangeType ( Op1 , UnderlyingType )
+
(System.SByte) System.Convert.ChangeType ( Op2 , UnderlyingType )
,
UnderlyingType
) ) ;
}

## Using the Code

`EnumOperators `

is most useful in a generic class where an enumeration is provided as a generic parameter.

T T3 EnumOperators<T>.Or ( T1 , T2 ) ;

You may also use it when not in that situation, in which case the only real benefit is the ease of performing mathematical operations on `enum`

values.

enum MyEnum { Value1 , Value2 , Value3 } ;
MyEnum e = EnumOperators<MyEnum>.Add ( MyEnum.Value1 , MyEnum.Value2 ) ;

## History

- 2008-05-22: First submitted