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!
looking for a better switch/case statement?
I find myself sometimes wishing I had a better switch/case construct in C#.
My requirements of "better" are:
- Case statements are automatically scoped
- I can switch on more than simple types
- I can have custom logic for my matching criteria
But why not just use if/else if/else...?
Good question, I'm glad you asked! However, I don't have an excellent answer other than personal preference. if/else if/else is okay sometimes, but other times I have a deterministic set of conditions, and a switch type construct feels more symmetrical to me. I loves me some symmetric feeling code, it just feels cleaner somehow.
So I'll present a somewhat contrived example just so you can see what the usage looks like, then we'll see the code.
Usage
public void DoSomething()
{
object foo = new object();
object bar = new object();
object baz = new object();
Switch<object>.On(baz)
.Case(x => x.Equals(foo), () =>
{
Console.WriteLine("came into foo case");
})
.Case(x => x.Equals(bar), () =>
{
Console.WriteLine("came into bar case");
})
.Default( () =>
{
Console.WriteLine("came into default case");
})
.Go();
}
The output from the above, is as you'd expect, the default case, since baz wasn't equal to foo or bar.
Implementation
public class Switch<T>
{
private T _target;
private List<KeyValuePair<Predicate<T>, Action>> _cases;
private Action _default;
public static Switch<T> On(T target)
{
return new Switch<T>(target);
}
public Switch(T target)
{
_target = target;
_cases = new List<KeyValuePair<Predicate<T>, Action>>();
}
public Switch<T> Case(Predicate<T> @case, Action action)
{
_cases.Add(new KeyValuePair<Predicate<T>, Action>(@case, action));
return this;
}
public Switch<T> Default(Action action)
{
_default = action;
return this;
}
public void Go()
{
foreach(var @case in _cases)
if (@case.Key(_target))
{
@case.Value();
return;
}
if (_default != null)
_default();
}
}
The only part I don't really like is having to kick it off with the Go() at the end. Any ideas for a better way to kick it off, or at least a better name for it (instead of Go).
Feedback? Ideas?
Update
Paul had a great idea a couple great ideas (see comments) on how to get rid of the Go() at the end, and to force Default to only appear at the end. Here is the new implementation. Usage is the same, just remove the Go().
public class Switch<T>
{
private T _target;
private bool _alreadyMatched;
public static Switch<T> On(T target)
{
return new Switch<T>(target);
}
public Switch(T target)
{
_target = target;
_alreadyMatched = false;
}
public Switch<T> Case(Predicate<T> @case, Action action)
{
if (!_alreadyMatched && @case(_target))
{
_alreadyMatched = true;
action();
}
return this;
}
public void Default(Action action)
{
if (!_alreadyMatched)
{
_alreadyMatched = true;
action();
}
return this;
}
}
chrome… c’mon
So I did what every other self-respecting geek has done in the past couple days... downloaded and installed chrome. I love my firefox experience at the moment (I have the exact add-ons/plugins I want/need to make browsing pleasurable and fast), but I thought I'd give it a shot.
However, I was greeted with this (and I can't seem to get anything other than this at the moment). Yes, I tried pressing reload or going to another page... didn't work.
That flat-out stinks (not because I personally can't use it, but because I've got no idea what to do to fix it, or what went wrong). Do I have to restart first? Do I have to install some WebKit thing first? Do I have to <insert other thing I shouldn't have to do here> first?
Maybe I'm just clueless, but this was a bad first impression.
