【问题标题】:Check whether PropertyInfo.SetValue will throw an ArgumentException检查 PropertyInfo.SetValue 是否会抛出 ArgumentException
【发布时间】:2015-12-23 10:01:50
【问题描述】:

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

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 可能会为SetValue 实际上成功 的某些类型返回false。 (这会导致我们在不需要时寻找手动转换,并且可能无法完全设置值。)

SetValue 的规范说

value不能转换为PropertyType的类型

这与可分配性并不完全相同。

(如果IsAssignableFromSetValue 失败的情况下返回true,那很好——手动转换仍然会发生。)

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

【问题讨论】:

  • 请举一个发生这种情况的实际例子。大概你有一个,所以应该很容易给出一个简短但完整的例子,记录IsAssignableFrom并成功调用SetValue。 (这可能只是原语的问题,可以相当容易地检测到......)
  • @Jon 我看到很多失败的情况是尝试将string 分配给无法从string 分配的自定义类型的属性(但我们有一个自定义转换器) .但是,这段代码可以将任何类型设置为任何其他类型,我不想破坏它,所以我对一般情况感兴趣。
  • 嗯,我很惊讶SetValue 会调用自定义转换器 - 但同样,一个完整的示例会说明这一点。
  • @Jon SetValue 不调用转换器。如果SetValue 失败,我们会去寻找一个。如果它失败了,我真的不认为我们做什么很重要——我只需要知道它是否与IsAssignableFrom 不一致。
  • 重要的是您声称SetValue 在使用无法转换为属性类型的类型调用时会成功。这对我来说似乎很不寻常,所以我要求举个例子。我不确定你为什么不愿意提供这样的例子。 (不清楚您所说的“这会导致我们寻找 TypeConverter”是什么意思 - 会是什么?那个代码在哪里?我希望 SetValue 失败,然后你可以寻找一个类型转换器...)

标签: c# reflection type-conversion


【解决方案1】:

正如您所怀疑的,short 类型的值可以通过反射分配给int 类型的属性,尽管typeof(int).IsAssignableFrom(typeof(short)) 返回false

查看ArgumentException的stacktrace:

  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(object, Binder, CultureInfo, BindingFlags) 中也有一些有趣的代码:

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

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

【讨论】:

  • 谢谢大佬。我自己刚刚找到了long/int 示例,但是您在查找逻辑的位置和逻辑方面做得比我好得多,而且缓存的想法很棒-我可能最终会这样做路线。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-10
  • 2018-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多