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
会调用自定义转换器-但是,一个完整的示例可以清楚地说明这一点。@JonSetValue
不会调用转换器。如果SetValue
失败,我们就去寻找一个。我真的认为如果它失败了,我们该怎么办并不重要-我只需要知道它是否与IsAssignableFrom
中的IsAssignableFrom不一致。重要的是,您声称当使用无法转换为属性类型的类型调用时,SetValue
会成功。这对我来说似乎很不寻常,所以我想举个例子。我不知道你为什么不愿意提供这样一个例子。(不清楚你所说的“这将导致我们寻找类型转换器”是什么意思-什么意思?代码在哪里?我希望SetValue失败,然后你可以寻找类型转换器…)谢谢,黑格。我自己刚刚找到了long
/int
示例,但您在查找逻辑的位置和内容方面比我做得好得多,而且缓存的想法非常好——我可能最终会走这条路。