private override

17Sep/081

a generic one-to-one mapping class

Another "tip/trick" style post, but thought it may prove useful to someone (or so I can find it again later!)...

Sometimes I find myself needing to map instances/values of one type to another.  For example, on my current project we have an enumeration in our communications layer, and a VERY similar one in the domain layer.  The reason for this is so we don't have to be referencing the communications layer version all over in the domain code.  So, there is naturally a one to one mapping between these enumerations.

Consider the following enumerations:

enum Polygons
{
    Triangle,
    Rectangle,
    Pentagon,
    Other
}

enum NumSides
{
    Three,
    Four,
    Five,
    More
}

This is the code I usually write to solve this problem:

private Polygons MapShapes( NumSides sides )
{
    switch( sides )
    {
        case NumSides.Three:
            return Polygons.Triangle;
        case NumSides.Four:
            return Polygons.Rectangle;
        case NumSides.Five:
            return Polygons.Pentagon;
        case NumSides.More:
            return Polygons.Other;
        default:
            throw new NotSupportedException(
                string.Format( "Mapping to Polygons from NumSides value {0}
                    is not supported", sides.ToString() ) );
    }
}

private NumSides MapShapes( Polygons polygon )
{
    switch( polygon )
    {
        case Polygons.Triangle:
            return NumSides.Three;
        case Polygons.Rectangle:
            return NumSides.Four;
        case Polygons.Pentagon:
            return NumSides.Five;
        case Polygons.Other:
            return NumSides.More;
        default:
            throw new NotSupportedException(
                string.Format( "Mapping to NumSides from Polygons value {0}
                    is not supported", polygon.ToString() ) );
    }
}

It turns out we had 3 said enumerations.  So picture the above, copied 3 times.  This thing was just begging for abstraction!

I came up with a Generic (proper) Map class that you can add mappings to and retrieve the corresponding mapped value of the other type... both directions.  Here is the implementation:

public class Map
{
    private Dictionary _oneToTwo;
    private Dictionary _twoToOne;

    public Map()
    {
        _oneToTwo = new Dictionary();
        _twoToOne = new Dictionary();
    }

    public Map Add( Type1 value1, Type2 value2 )
    {
        _oneToTwo.Add( value1, value2 );
        _twoToOne.Add( value2, value1 );
        return this;
    }

    public Type1 this[Type2 value]
    {
        get
        {
            if( !_twoToOne.ContainsKey( value ) )
            {
                throw new NotSupportedException(
                    string.Format( "Mapping from type {0} to type {1} with
                                    value {2} is not defined",
                        typeof( Type2 ).Name,
                        typeof( Type1 ).Name,
                        value.ToString() ) );
            }
            return _twoToOne[value];
        }
    }

    public Type2 this[Type1 value]
    {
        get
        {
            if( !_oneToTwo.ContainsKey( value ) )
            {
                throw new NotSupportedException(
                    string.Format( "Mapping from type {0} to type {1} with
                                    value {2} is not defined",
                        typeof( Type1 ).Name,
                        typeof( Type2 ).Name,
                        value.ToString() ) );
            }
            return _oneToTwo[value];
        }
    }
}

And, here it is in action:

Map<NumSides, Polygons> shapeMap = new Map<NumSides, Polygons>()
                                    .Add( NumSides.Three, Polygons.Triangle )
                                    .Add( NumSides.Four, Polygons.Rectangle )
                                    .Add( NumSides.Five, Polygons.Pentagon )
                                    .Add( NumSides.More, Polygons.Other );

// shape1 value is Polygons.Rectangle
Polygons shape1 = shapeMap[NumSides.Four]; 

// shape2 value is NumSides.Three
NumSides shape2 = shapeMap[Polygons.Triangle];

This not only works with enumerations, but any type.  I see an extension to this possibly allowing for a default mapping, and possibly constraining Type1 and Type2 to be IComparable or something like that as well, so you can have more control over your matching than whatever the default matching behavior that the Dictionary uses is.

That's it... enjoy!

Tagged as: , 1 Comment
4Sep/084

looking for a better switch/case statement?

Tagged as: , 4 Comments
26Aug/082

tfs – using winmerge as your merge/diff tool

Tagged as: , 2 Comments
26Aug/080

tfs – error exporting queries to excel

Tagged as: , , No Comments
22Aug/081

Going Medieval on Your App with Castle MonoRail – Indy ALT.NET

22Aug/080

singly implemented interface… addendum

Tagged as: , No Comments
16May/082

indy alt.net

16May/081

singly implemented interface… why/why not

Tagged as: , 1 Comment
15Jan/072

Audience

Filed under: All, Tech 2 Comments
4Dec/0621

Mock Objects… Firing Events

Filed under: All, Tech 21 Comments