Check if PropertyInfo.SetValue will throw an ArgumentException

I inherited some code that is trying to set a property:

object target = ... // some instance on which we want to set a property object value = ... // some value - in this case, a string var propertyInfo = ... // some property of target - in this case, not a string try { propertyInfo.SetValue(obj, value, null); } catch (ArgumentException) { // We go off and look for our own way of converting between // the type of value and the type of the property. } 

In current use, the exception is caught and a lot is thrown, so I would first check:

 if (propertyInfo.PropertyType.IsAssignableFrom(value.GetType()) { // Try/catch as above } else { // Do the manual conversion as if the exception had been thrown. } 

It works a lot faster. However, my only problem is that IsAssignableFrom can return false for some pair of types, where SetValue will actually succeed. (This will force us to look for manual conversion when we do not need it, and may not be able to set the value at all.)

The specification for SetValue states

Value

cannot be converted to type PropertyType

which is not exactly the same as the purpose.

(If IsAssignableFrom returns true when SetValue fails, that's fine - manual conversion will happen anyway.)

Can anyone confirm if this is possible or not?

+7
reflection c # type-conversion
source share
1 answer

As you suspected, a value of type short assigned to a property of type int through reflection, although typeof(int).IsAssignableFrom(typeof(short)) returns false .

Looking at the stack of ArgumentException :

  at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast) 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.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index) at System.Reflection.PropertyInfo.SetValue(Object obj, Object value) at ConsoleApplication3.Program.Main(String[] args) in ... 

An exception is RuntimeType.TryChangeType in RuntimeType.TryChangeType . Looking at the source in mscorlib:

 // System.RuntimeType [SecurityCritical] private object TryChangeType(object value, Binder binder, CultureInfo culture, bool needsSpecialCast) { ... if (this.IsInstanceOfType(value)) { return value; } ... if (RuntimeType.CanValueSpecialCast(valueType, this)) { ... } throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, Environment.GetResourceString("Arg_ObjObjEx"), value.GetType(), this)); } 

Unfortunately, there are many internal functions called to determine whether to throw an ArgumentException . I suspect that primitive conversion is being processed somewhere around this function:

 // System.RuntimeType [SecurityCritical] [MethodImpl(MethodImplOptions.InternalCall)] private static extern bool CanValueSpecialCast(RuntimeType valueType, RuntimeType targetType); 

There is another interesting code in System.RuntimeType.CheckValue(object, Binder, CultureInfo, BindingFlags) :

 bool flag = base.IsPointer || this.IsEnum || base.IsPrimitive; if (flag) { ... if (RuntimeType.CanValueSpecialCast(valueType, this)) { ... } } 

So it looks like pointer, enumeration, and primitive types are handled differently. I let you try to copy this logic. It would be easier to just use IsAssignableFrom and handle the whole types separately. Handling pointer, enumeration, and primitive type looks very complicated, I will just step back from try-catch there. However, you can cache if an argument error occurs, so subsequent calls to the same method with the same parameters may be somewhat faster.

+1
source share

All Articles