views:

62

answers:

1

Firstly, I've asked this question elsewhere, but meta.stackoverflow.com seems to think that asking the same questions elsewhere is fine, so here goes. I'll update both if/when I get an answer.

I'm doing a lot of conversion between simple types and I wanted to handle it as nicely as possible. What I thought would be nice would be to be able to do something like this:

var converter = GetMySingletonConverterRegistryPlease();
var somePoint = GetMeSomePointFromSomewhereThanks();

converter.Register<Point, List<int>>( 
  point => new List<int>{ point.X, point.Y } 
); 

var someInts = somePoint.To<List<int>>();

So, I came up with the following. Which I'm not at all happy with and would like some advice on (I'll enumerate why I don't like it after the code snippet)

public sealed class TypeConverterRegistry : TypeConverterRegistryBase {
  public static readonly TypeConverterRegistry Instance = new 
    TypeConverterRegistry();
  static TypeConverterRegistry() {}
  TypeConverterRegistry() {}
}

public abstract class TypeConverterRegistryBase {
  private readonly Dictionary<object, Delegate> _converters = new 
    Dictionary<object, Delegate>();

  public void Register<TFrom, TTo>( Func<TFrom, TTo> converter ) {
    var key = new { From = typeof( TFrom ), To = typeof( TTo ) };
    _converters[ key ] = converter;
  }

  public TTo ConvertTo<TTo>( object from ) {
    var key = new { From = from.GetType(), To = typeof( TTo ) };

    if( !_converters.ContainsKey( key ) ) 
      throw new KeyNotFoundException( 
        "No converter has been registered that takes a " + key.From + 
        " and returns a " + key.To );

    var converter = _converters[ key ];
    return (TTo) converter.DynamicInvoke( from );
  }
}

public static class Extensions {
  public static TTo To<TTo>( this object value ) {
    return TypeConverterRegistry.Instance.ConvertTo<TTo>( value );
  }
}  

I don't like what I've done here, because it's not strongly typed/constrained, it's easy to accidentally misuse it and get a runtime exception, I'm using object as a catch all, and I also have a bad feeling about the way I'm calling DynamicInvoke and boxing the result. You guys will no doubt see even more problems than that! Also I feel bad about making an extension method on object.

What I do like is the resulting syntax!

So I'd really appreciate any advice, even if it's just a nudge in the right direction.

Or reassurance that it's not such a bad way to do it after all :P

+3  A: 

Take a look at AutoMapper (http://automapper.codeplex.com/). While different in intent, your project shares a lot of commonality and you may be able to get a lot from their patterns & code.

In terms of runtime exceptions, this may come down to unit testing since you're relying on the user registering the type before using it. There's no way around that and it's the same thing AutoMapper does but they provide some testing stuff in there too.

I would highly recommend rewriting the ConvertTo method to be generic so that the from parameter is generic instead of type object. You could do that something like:

  public TTo ConvertTo<TFrom, TTo>( TFrom from ) {
    var key = new { From = typeof( TFrom ) To = typeof( TTo ) };

    if( !_converters.ContainsKey( key ) ) 
      throw new KeyNotFoundException( 
        "No converter has been registered that takes a " + key.From + 
        " and returns a " + key.To );

    var converter = _converters[ key ] as Func<TFrom, TTo>;
    return converter(from);
  }

this would also solve your issue with the extension method. You'd now invoke using:

public static TTo Convert<TFrom, TTo>(this TFrom value){
return TypeConverterRegistry.Instance.Convert<TFrom, TTo>(value);
}
statichippo
This is actually how I originally had that code, but I've fallen in love with the clarity of something = somethingElse.To<SomeType>(); Perhaps I just need to realize that I can't have my pie and eat it too? I'll await any other suggestions with baited breath, but sadly I suspect that you may be right and that having the syntactic sugar my way is unrealistic in terms of code quality :)
nrkn
Oh, also? Already using AutoMapper for other things :) I'm surprised it didn't already occur to me how much overlap there is, and I'll look into it further.
nrkn
I understand why it might be cute to use an extension method to say SomeObj.To<OtherType>() but Automapper's pattern for this works well and is also pretty IMO -- just a static method, something in your case like Converter.Convert<TFrom, TTo>(TFrom from, TTo to).
statichippo
Yeah, I think I'm being too precious. Thanks for your input!
nrkn