检查PropertyInfo.SetValue是否会抛出ArgumentException

Raw*_*ing 7 c# reflection type-conversion

我继承了一些试图设置属性的代码:

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.
}
Run Code Online (Sandbox Code Playgroud)

在当前使用中,异常被捕获并抛出很多,所以我想首先进行检查:

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

这运行得更快.但是,我唯一担心的是,对于某些实际上会成功的类型,IsAssignableFrom可能会返回.(这会导致我们在不需要时查找手动转换,并且可能无法完全设置值.)falseSetValue

规格SetValue

value无法转换为PropertyType的类型

这与可分配性不完全相同.

(如果在失败的情况下IsAssignableFrom返回,那很好 - 手动转换仍然会发生.)trueSetValue

有人可以向我确认这是否可行?

Tam*_*dus 2

正如您所怀疑的,尽管returns ,但可以通过反射将type 的值short分配给 type 的属性。inttypeof(int).IsAssignableFrom(typeof(short))false

查看堆栈跟踪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 ...
Run Code Online (Sandbox Code Playgroud)

异常被抛出RuntimeType.TryChangeType。查看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));
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,有许多内部函数被调用来确定是否ArgumentException应该抛出 an 。我怀疑基元的转换是在该函数周围处理的:

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

还有一些有趣的代码System.RuntimeType.CheckValue(object, Binder, CultureInfo, BindingFlags)

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

所以看起来指针、枚举和原始类型的处理方式不同。我不鼓励你尝试复制这个逻辑。IsAssignableFrom仅使用, 并单独处理整数类型可能会更容易。处理指针、枚举和原始类型参数看起来非常复杂,我会回退到try-catch那里。但是,如果发生参数错误,您可以缓存,因此对具有相同参数类型的同一方法的后续调用可能会更快一些。