I have some code (which works fine) that looks something like this:
int integer = 42;
decimal? castTo = integer;
Then I wanted to do something similar with reflection, with some code that looks like this:
object value = source; // source was an int originally
var parameters = new object[1];
...
parameters[0] = value;
var setMethod = property.GetSetMethod();
// Call the set method, which takes a decimal? as a parameter
setMethod.Invoke(o, parameters);
When I do this, I get:
failed: System.ArgumentException : Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1[System.Decimal]'.
at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
Why would an implicit type conversion that works fine elsewhere fail with reflection? Is there a trick to using reflection to perform this conversion?
Edit: Thanks all for the responses. Here is the solution I came up with, based on the answers:
private object Convert(object source, Type destinationType)
{
if (source == null)
{
return null;
}
var sourceType = source.GetType();
// unwrap nullable types
var nullableType = Nullable.GetUnderlyingType(destinationType);
if(nullableType != null)
{
destinationType = nullableType;
}
nullableType = Nullable.GetUnderlyingType(sourceType);
if(nullableType != null)
{
sourceType = nullableType;
}
var implicitCastMethod =
destinationType.GetMethod("op_Implicit",
new[] { sourceType } );
if(implicitCastMethod == null)
{
return null;
}
return implicitCastMethod.Invoke(null, new[] { source });
}
Edit #2: I wish someone had mentioned System.Convert.ChangeType()
, which handles these cases, and more. It turns out that op_Implicit
can only convert to less restrictive numeric types. (of course, hence the "Implicit" in the name). In other words, the first solution worked for int
→ decimal?
but not decimal?
→ int
. (It seems that I would need to change this code to also try op_Explicit
if the implicit cast failed, if I wanted to be able to handle a conversion from decimal?
back to int
.)
Since System.Convert.ChangeType()
doesn't work with Nullable<>
types, I finally ended up using some code similar to what I found here (slightly modified):
private static object Convert(object source, Type destinationType)
{
if(destinationType == null)
{
throw new ArgumentNullException("destinationType");
}
if(destinationType.IsGenericType &&
destinationType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (source == null)
{
return null;
}
destinationType = Nullable.GetUnderlyingType(destinationType);
}
return System.Convert.ChangeType(source, destinationType);
}
You will have to do the conversion yourself, as the compiler handles the cast in a non-reflection environment. As reflection code basically is evaluating types and objects like the compiler does, you will have to look for a method named op_implicit
with the needed parameters (in your case Int32
) on your object and invoke it. After that, you can invoke the property accessor. A possible way would be the following:
//search for an implicit cast operator on the target type
MethodInfo[] methods = targetType.GetMethods();
foreach(MethodInfo method = source.GetType().GetMethod("op_Implicit"))
{
if (method.Name == "op_Implicit")
{
ParameterInfo[] parameters = method.GetParameters();
if (parameters.Length == 1 && parameters[0].ParameterType == value.GetType())
{
value = method.Invoke(obj,new object[]{value});
break;
}
}
}
The runtime doesn't know about implicit conversions.
You can call op_Implicit
or another conversion method through reflection, but that way you'll only get the specific conversion semantics that you implement. If you're using C# 4.0, I'd recommend using the "dynamic" type here, since it will implement the C# conversion semantics automatically.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With