Click here to Skip to main content
15,885,309 members
Articles / Programming Languages / C#
Article

EnumOperators

Rate me:
Please Sign up or sign in to vote.
4.50/5 (11 votes)
22 May 2008CPOL2 min read 33.8K   145   30   5
A class to help ease the burden of not being able to specify enum as a generic constraint

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.

C#
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.

C#
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.

C#
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.

C#
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.

C#
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 ;
        }

        // Case blocks for the other supported types snipped
    }

    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.

C#
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.)

C#
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.

C#
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.

C#
enum MyEnum { Value1 , Value2 , Value3 } ;

MyEnum e = EnumOperators<MyEnum>.Add ( MyEnum.Value1 , MyEnum.Value2 ) ;

History

  • 2008-05-22: First submitted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
BSCS 1992 Wentworth Institute of Technology

Originally from the Boston (MA) area. Lived in SoCal for a while. Now in the Phoenix (AZ) area.

OpenVMS enthusiast, ISO 8601 evangelist, photographer, opinionated SOB, acknowledged pedant and contrarian

---------------

"I would be looking for better tekkies, too. Yours are broken." -- Paul Pedant

"Using fewer technologies is better than using more." -- Rico Mariani

"Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’" -- Steve McConnell

"Every time you write a comment, you should grimace and feel the failure of your ability of expression." -- Unknown

"If you need help knowing what to think, let me know and I'll tell you." -- Jeffrey Snover [MSFT]

"Typing is no substitute for thinking." -- R.W. Hamming

"I find it appalling that you can become a programmer with less training than it takes to become a plumber." -- Bjarne Stroustrup

ZagNut’s Law: Arrogance is inversely proportional to ability.

"Well blow me sideways with a plastic marionette. I've just learned something new - and if I could award you a 100 for that post I would. Way to go you keyboard lovegod you." -- Pete O'Hanlon

"linq'ish" sounds like "inept" in German -- Andreas Gieriet

"Things would be different if I ran the zoo." -- Dr. Seuss

"Wrong is evil, and it must be defeated." –- Jeff Ello

"A good designer must rely on experience, on precise, logical thinking, and on pedantic exactness." -- Nigel Shaw

“It’s always easier to do it the hard way.” -- Blackhart

“If Unix wasn’t so bad that you can’t give it away, Bill Gates would never have succeeded in selling Windows.” -- Blackhart

"Use vertical and horizontal whitespace generously. Generally, all binary operators except '.' and '->' should be separated from their operands by blanks."

"Omit needless local variables." -- Strunk... had he taught programming

Comments and Discussions

 
GeneralMy vote of 5 Pin
Doncp6-Dec-10 5:44
Doncp6-Dec-10 5:44 
Good, especially with lippie's tip.
GeneralCool, but here's a tip Pin
leppie22-May-08 12:31
leppie22-May-08 12:31 
GeneralRe: Cool, but here's a tip Pin
PIEBALDconsult22-May-08 13:14
mvePIEBALDconsult22-May-08 13:14 
GeneralOh and [modified] Pin
PIEBALDconsult22-May-08 14:11
mvePIEBALDconsult22-May-08 14:11 
GeneralRe: Oh and Pin
leppie22-May-08 17:48
leppie22-May-08 17:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.