我继承了一些尝试设置属性的代码:
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
的规范说
value 无法转换为PropertyType
的类型
与可转让性不完全相同。
(如果IsAssignableFrom
在true
失败的情况下返回SetValue
,那很好 - 手动转换仍然会发生。)
有人可以向我确认这是否可行?
答案 0 :(得分:2)
如您所料,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(object, Binder, CultureInfo, BindingFlags)
中还有一些有趣的代码:
bool flag = base.IsPointer || this.IsEnum || base.IsPrimitive;
if (flag)
{
...
if (RuntimeType.CanValueSpecialCast(valueType, this))
{
...
}
}
因此看起来像指针,枚举和基本类型以不同的方式处理。我不鼓励你试着复制那个逻辑。使用IsAssignableFrom
可能更容易,并分别处理整数类型。处理指针,枚举和原始类型参数看起来非常复杂,我只会回到try-catch
那里。但是,如果发生参数错误,您可以缓存,因此对具有相同参数类型的相同方法的后续调用可能会更快。