For my Translation Tester I originally wanted a user to be able to add a mapping between two properties of different types, so for example they could say that a property of type Int16 would be directly assigned to a property of type Int32 by the translator. This turned out to be real tricky to get working, and after much analysis I’m not sure it should even be done as part of a ‘simple mapping’.
The problem is that the types of the parameters are found using reflection, so you then need ways to tell whether there are any commonly supported ways to convert one type to another, and then a way to actually do the conversion to verify that the mapping was fulfilled.
To expand the problem let’s list some of the possible scenarios:
- Primitive type that can be implicitly cast to another primitive type (e.g. Int16 assigned to Int32)
- Primitive type that can explicitly cast to another primitive type (e.g. Int32 to Int16)
- Primitive/Struct/Class to Primitive/Struct/Class where an implicit cast operator has been added
- Primitive/Struct/Class to Primitive/Struct/Class where an explicit cast operator has been added
This gets pretty confusing, particularly with all the permutations for the last two.
When dealing solely with the primitive types there is an interface IConvertible that they all support, that allows conversion between the primitive types. The Convert.ChangeType() method simply provides a nice wrapper around the IConvertible interface. One problem here is that for several of the primitive types they have the whole interface, but individual conversions will always fail at run-time (e.g. decimal to DateTime).
So this facility can be used for primitive types at comparison time, but to determine whether a simple mapping should be allowed you’d have to actually attempt a dummy conversion, using something like Activator.CreateInstance().
I posted a question on StackOverflow.com, one of the answers suggested I look into TypeConverters. From what I can see the built in TypeConverters are focussed on converting from and to strings. This facility could be used to provide Converters for primitive types and even allow users to add converters for custom types. But this all seems rather heavy.
This post contains an interesting implementation for dynamic casting based on looking for implicit (and if necessary explicit) cast operator methods on the reflected types and using them to perform a cast. One problem is that the primitive types don’t use cast operators to cast between themselves, rather they use the IConvertible interface. So this wouldn’t work for primitive-to-primitive conversions.
What I’ve done
This all seemed to be getting really confusing, so what I’ve done at the moment is only allow simple mappings to be added where the types are identical. This makes the code a lot easier, and also removes any magic being hidden from the user; if they want to map a property to a property of another type they can specify this using a complex mapping (predicate).
What I might do
Having written all this down I think it might be possible to do what I originally intended. using the following steps to add a mapping (confirm that it’s possible):
- If both types are primitives – Consult a local dictionary of valid mappings
- else use the Dynamic Cast method to determine if there is a cast operator specified
- If no cast operator specified see if there is a custom converter (perhaps based on TypeConverter).
Then the following steps to verify the mapping
- if both types are primitives use Convert.ChangeType() to convert the from type to the to type.
- else use the cast operator or custom converter specified.
Another thought I’ve had is that you might want to limit the allowed conversions to just implicit conversions (where you wont lose precision/data), this could be achieved by splitting the primitive mappings into 2 lists, and separating the dynamic cast into 2 checks.