Introduction
The System.Convert class is great for converting from one type to another but as some people may have run across, it can only convert types that implement IConvertible, even if the class you are trying to convert to has explicit or implicit conversion operators.
With generics and dynamic types available in .NET today, programmers find themselves working with unknown types more often. Sometimes we need to be able to check if that type can be converted to another type or if that type supports a defined cast operator for another type.
Additionally, this utility presents an expansion on the default keyword, as it stands default can create one of two values, 0 (if the type is primitive value type), or null (if the type is a reference type). The utility here will create a default instance of a type if it has a default constructor, or returns null if it does not have one.
Background
System.Convert
can convert any type that implements IConvertible
, but does not support types that do not implement that interface even if the type has implicit or explicit operators to convert to other IConvertible
types. This is because the System.Convert
utility first checks for the implementation of IConvertible
before attempting to convert and throws an exception if the types do not implement that interface.
So what happens when you are working with unknown types at compile time and need to verify that those types can convert to another type? Either you can say that the type you are working with needs to support IConvertible
(which only supports primitive types), or you can just attempt a cast and check the exception.
The ConvertAny
utility gets around this by providing the following methods:
CanConvert<ToType>(object)
- Tests if the object is convertible to ToType. Returns true if object can be converted or false if it cannot be.
CanConvert(Type, Type)
- Tests if the from type can be converted to the to type. This works on type references only and does not require an object reference.
TryConvert<ToType>(object, out ToType)
- Attempts to convert the type, much like the TryParse methods that you find on many primitive types. If the conversion fails ToType will be the
default
(ToType) (the system one, not the utility one).
-
Convert<ToType>(object)
- This converts the type and will throw an exception if the object cannot be converted to the ToType.
ConvertByType(object, Type)
- This converts the object using a
Type
reference. If the object cannot be converted to the type it will throw an exception.
Default<T>()
- Creates a default instance of the type. If the type is a primitive type it has the same behavior as the default keyword. If the type has a default constructor, it will create an instance of the type. If the type does not have a default constructor this will return null.
DefaultByType(Type)
- Same as above only it uses a
Type
reference instead of a generic.
How type checking/conversion works
Conversion can be an expensive operation so it is important to try the least expensive operations to the more expensive operations in that order. This will keep the speed up in the case that objects are easily convertible to another type.
Lets take a look at the CanConvert<ToType>(object)
code:
public static bool CanConvert<ToType>(object value)
{
if (value == null)
throw new ArgumentNullException("value", StringResources.ArgumentNullException);
Type tType = typeof(ToType);
Type fType = value.GetType();
if (fType == tType)
return true;
if (typeof(IConvertible).IsAssignableFrom(tType) &&
typeof(IConvertible).IsAssignableFrom(fType))
{
return true;
}
try
{
MethodInfo mi = tType.GetMethods().FirstOrDefault(m =>
(m.Name == "op_Explicit" || m.Name == "op_Implicit") &&
m.ReturnType == tType &&
m.GetParameters().Length == 1 &&
m.GetParameters()[0].ParameterType == fType
);
if (mi == null)
{
mi = fType.GetMethods().FirstOrDefault(m =>
(m.Name == "op_Explicit" || m.Name == "op_Implicit") &&
m.ReturnType == tType &&
m.GetParameters().Length == 1 &&
m.GetParameters()[0].ParameterType == fType
);
}
if (mi != null)
return true;
return false;
}
catch
{
return false;
}
}
This code pattern is the same throughout the other functions, only modified to either return a value, a boolean
, or use an out parameter for the TryConvert
methods.
As you can see, the first thing that we check is if the types are the same, if they are just return true. Next we will check to see if the types are both IConvertible, and if they are we know that System.Convert can easily convert them so we return true.
The last part is where the meat is, if you read the comments inside the try you'll see that casting boxed object can fail even if those objects define operators to convert from one to the other. This was a good couple hours of frustration for me because the cast would work fine in the Watch window, and I can see that the object has the conversion operators, but it would throw an exception every time.
In order to get around this, we have to actually invoke the operator through reflection instead of using the cast. It would have been easy to write (ToType)value
inside the try and if that worked return true
, or false
inside the catch, however that doesn't work.
Since the conversion cast may be defined on either type, we need to check both types for the cast operators. If it isn't found on the first type, we will check the second type for the operator. If either one of those checks finds the operator, it works.
The new default
The implementation of the default keyword has irked me for some time. There are only three possible options for the default, either it returns 0 for primitive types like int
, long
, double
, float
, it returns the basic constructed type for structs (since structs are not nullable), or it returns null
for reference types.
It would have been nice that for reference types if the type contains a default constructor, that it returns a default instance of the type. I needed this behavior in some of my code, so I wrote an extension to the Type class that can create default instances:
public static object CreateDefault(this Type t)
{
return ConvertAny.DefaultByType(t);
}
Which calls this function inside the ConvertAny
class:
public static T Default<T>()
{
if (typeof(T).IsPrimitive)
return default(T);
ConstructorInfo cInfo = typeof(T).GetConstructor(Type.EmptyTypes);
if (cInfo == null)
return default(T);
return (T)cInfo.Invoke(null);
}
public static object DefaultByType(Type type)
{
MethodInfo generic = _genericDefaultMethod.MakeGenericMethod(type);
return generic.Invoke(null, null);
}
The _genericDefaultMethod
is simply a cached reference to the Default<T>
function that is created when the ConvertAny
static constructor is run. While pretty simple, it does some interesting things. First it checks to see if the type is a primitive, and if it is just returns the normal default(T)
for that type. Next it looks for a constructor that has no arguments (default constructor) and if it finds one, will return a default instance of that type.<code>
Using the code
Included in the project is the unit tests that prove the operation of the class, it provides many examples of how to use the different functions since it includes a test for each public method defined in both the ConvertAny
and the TypeExtensions
classes. Here we will just go over some basic uses:
Finding if a type can be converted to another type
In order to do this, you can either use the CanConvert<ToType>(object)
function if you have a strong type reference, or you can use the CanConvert(Type, Type)
function if you only have a reference to the Type
objects for the values.
ConvertAny.CanConvert<bool>(1);
ConvertAny.CanConvert<TestCastibleClass>((double)1.0);
ConvertAny.CanConvert(typeof(int), typeof(bool));
ConvertAny.CanConvert(typeof(TestCastibleClass), typeof(double));
Attempt to convert one type to another
If you want to combine converting and the check into a single call (and better performance) then you can use the TryConvert<ToType>(object, out ToType)
method. Why doesn't the library have a TryConvertByType(object, Type, out object)
method? I leave that as an exercise for you, in order to implement it look at the Convert and ConvertByType methods.
bool b = false;
ConvertAny.TryConvert<bool>(1, out b);
TestCastibleClass tc = null;
ConvertAny.TryConvert<TestCastibleClass>(15.2, out tc);
Convert a value
If you want to skip checking and just convert the value, you can use the Convert<ToType>(object)
or ConvertByType(object, Type) methods. Be aware though, these will throw an InvalidCastException if they fail to convert the object to the desired type.
ConvertAny.Convert<bool>(0);
ConvertAny.Convert<TestCastibleClass>(12.2);
ConvertAny.ConvertByType(1, typeof(bool));
ConvertAny.ConvertByType(new TestCastibleClass(), typeof(double));
Working with the type extensions
There are two extensions to the type as outlined earlier, the first is to get the default instance of a type CreateDefault
and the second is to check if the type is convertible to another type, CanConvertTo(Type)
.
object o = typeof(TestCastibleClass).CreateDefault();
typeof(int).CanConvertTo(typeof(double));
typeof(TestCastibleClass).CanConvertTo(typeof(double));
Points of Interest
Why go through all the trouble when .NET introduced the dynamic
runtime types in version 4?
Originally I designed this class to convert any dynamic
type to any other type. To be honest the class was much simpler to implement with the dynamic
keyword than it was with the object
one, type casting on boxed types worked, and conversion was simple.
Then I designed my unit tests (yes, I know, tests should be done first), and was shocked when I ran them. Some of the conversion routines took 100 milliseconds to run. I had to walk away from my computer for a good 30 minutes, and when I sat down to research it I found the problem. Dynamic types in .NET are extremely fast if they can be cached. When the DLR encounters a dynamic type it adds the site to a lookup so that the next time it doesn't have to build the type information. It can't do this in a static class that gets called with dynamic parameters because the parameters change.
This caused a huge performance hit, since my original application for this class was to be used in a scripting library that did not work on statically typed objects and may include hundreds of checks/conversions per run. When I re-wrote the ConvertAny
utility to use objects and reflection, the performance increased roughly 100 times (now takes 1ms or less to run particular tests).
The lesson here is that while dynamic
may make your life easier, you must be aware of performance implications when working with it. Test throughly (even after you wrote the code) and make sure that your performance is on par with what your application requires.
History
1/13/2014 - Initial Version
1/14/2014 - Added null reference checking and reduced the use of typeof
in the functions, thanks to Oliver Albrecht.