C# 检查PropertyInfo.SetValue是否会引发ArgumentException

C# 检查PropertyInfo.SetValue是否会引发ArgumentException,c#,reflection,type-conversion,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 { proper

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

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.
}
在当前使用中,异常会被捕获并抛出很多次,因此我想先检查一下:

if (propertyInfo.PropertyType.IsAssignableFrom(value.GetType())
{
    // Try/catch as above
}
else
{
    // Do the manual conversion as if the exception had been thrown.
}
这运行得快得多。然而,我的一个担忧是,
IsAssignableFrom
可能会返回
false
,对于某些类型,
SetValue
实际上会成功。(这将导致我们在不需要手动转换时寻找手动转换,并且可能无法完全设置值。)

SetValue
的规范中说

值无法转换为PropertyType的类型

这与可转让性并不完全相同

(如果
IsAssignableFrom
SetValue
失败的情况下返回
true
,则可以-手动转换仍然会发生。)


有人能帮我确认一下这是否可能吗?

正如您所怀疑的,类型
short
的值可以通过反射分配给类型
int
的属性,尽管
typeof(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 ...
异常在
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));
}
不幸的是,有许多内部函数被调用来确定是否应该抛出
ArgumentException
。我怀疑原语的转换是围绕这个函数进行的:

// System.RuntimeType
[SecurityCritical]
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool CanValueSpecialCast(RuntimeType valueType, RuntimeType targetType);
System.RuntimeType.CheckValue(对象、绑定器、文化信息、绑定标志)中也有一些有趣的代码。

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

所以看起来指针、枚举和基元类型的处理方式不同。我劝你不要照搬那种逻辑。只使用
IsAssignableFrom
并单独处理整型可能更容易。处理指针、枚举和基元类型参数看起来非常复杂,我只想回到那里的
try catch
。但是,如果发生参数错误,您可以缓存,以便后续对具有相同参数类型的同一方法的调用可能会更快。

请给出一个实际发生这种情况的示例。假设您有一个,所以应该很容易给出一个简短但完整的示例,其中记录
IsAssignableFrom
,并成功调用
SetValue
。(这可能只是原语的问题,可以很容易地检测到…@Jon我看到失败的例子是尝试将
字符串
分配给自定义类型的属性,该属性不能从
字符串
分配(但我们有一个自定义转换器)。但是,这段代码可以将任何类型设置为任何其他类型,我不想破坏它,所以我对一般情况感兴趣。我很惊讶
SetValue
会调用自定义转换器-但是,一个完整的示例可以清楚地说明这一点。@Jon
SetValue
不会调用转换器。如果
SetValue
失败,我们就去寻找一个。我真的认为如果它失败了,我们该怎么办并不重要-我只需要知道它是否与
IsAssignableFrom
中的IsAssignableFrom不一致。重要的是,您声称当使用无法转换为属性类型的类型调用时,
SetValue
会成功。这对我来说似乎很不寻常,所以我想举个例子。我不知道你为什么不愿意提供这样一个例子。(不清楚你所说的“这将导致我们寻找类型转换器”是什么意思-什么意思?代码在哪里?我希望SetValue失败,然后你可以寻找类型转换器…)谢谢,黑格。我自己刚刚找到了
long
/
int
示例,但您在查找逻辑的位置和内容方面比我做得好得多,而且缓存的想法非常好——我可能最终会走这条路。