Click here to Skip to main content
Click here to Skip to main content

EnumOperators

, 22 May 2008
Rate this:
Please Sign up or sign in to vote.
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.

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

        // 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.

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

License

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

About the Author

PIEBALDconsult
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
 
---------------
 
"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

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

 
"We learn more from our mistakes than we do from getting it right the first time."
 
My first rule of debugging: "If you get a different error message, you're making progress."
 
My golden rule of database management: "Do not unto others' databases as you would not have done unto yours."
 
My general rule of software development: "Design should be top-down, but implementation should be bottom-up."

Comments and Discussions

 
GeneralMy vote of 5 PinmemberDoncp6-Dec-10 5:44 
GeneralCool, but here's a tip Pinmember leppie 22-May-08 12:31 
GeneralRe: Cool, but here's a tip PinmemberPIEBALDconsult22-May-08 13:14 
GeneralOh and [modified] PinmemberPIEBALDconsult22-May-08 14:11 
GeneralRe: Oh and Pinmember leppie 22-May-08 17:48 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140721.1 | Last Updated 22 May 2008
Article Copyright 2008 by PIEBALDconsult
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid