如何从reflection中执行显式操作转换?

我想使用reflection,并使用reflection进行隐式或显式转换。

鉴于我已经用这种方式定义了Foo

public class Foo { public static explicit operator decimal(Foo foo) { return foo.Value; } public static explicit operator Foo(decimal number) { return new Foo(number); } public Foo() { } public Foo(decimal number) { Value = number; } public decimal Value { get; set; } public override string ToString() { return Value.ToString(); } } 

当我运行此代码时

 decimal someNumber = 42.42m; var test = (Foo)someNumber; Console.WriteLine(test); // Writes 42.42 No problems 

当我尝试使用Foo定义一个类作为成员类型并使用reflection来设置它时。 我得到以下例外。

 Error : Object of type 'System.Decimal' cannot be converted to type 'Foo'. 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.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) 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) 

这是我用于使用reflection设置属性的代码

 public class FooComposite { public Foo Bar { get; set; } } var properties = typeof(FooComposite).GetProperties(); var testFoo = new FooComposite(); foreach(var propertyInfo in properties) { propertyInfo.SetValue(testFoo, 17.17m, null); // Exception generated on this line } Console.WriteLine(testFoo.Bar); // Never gets here 

我该怎么做这个转换?

那么它与你的非reflection代码没有什么不同,你仍然需要显式地将数字转换为Foo

 propertyInfo.SetValue(testFoo,(Foo)17.17m, null); 

实例: http : //rextester.com/rundotnet?code = BPQ74480

出于兴趣,我尝试了一些替代方案。

  1. 使它成为Fooimplicit演员 – 不起作用,同样的错误直播
  2. 使用Convert.ChangeType(17.17m,typeof(Foo)) – 也不起作用。 生活

今天我在尝试按名称复制字段到对象时查看了这个问题。 我很失望地看到所选答案是“你只能明确地调用一个明确的运算符”。 毕竟,其他任何事情都可以通过反思来完成。

我的问题是一种reflection方法,由于类型复杂,试图在两个类之间进行深层复制。 我试图定义一个explicit operator转换,但它似乎没有被调用,所以我想出了一种通过reflection得到它的方法。 使用其他一些关于调用静态方法的研究,我发现当将存储在pSource中的复杂类型复制到属性pDest中的不同类型时,这对我有用。 pDest中的类型具有pSource类型的转换。

 MethodInfo[] static_methods = pDest.PropertyType.GetMethods(System.Reflection.BindingFlags.Static | BindingFlags.Public); if (static_methods != null) { foreach (MethodInfo method in static_methods) { if(method.Name== "op_Explicit") // this is a constant { // for explicit operators ParameterInfo[] paramSet = method.GetParameters(); if ((paramSet != null) && (paramSet.Length == 1)) { if (paramSet[0].ParameterType == pSource.PropertyType) // match the types! { pDest.SetValue( // Destination prop dstVar, // Destination instance method.Invoke( // converter method null, // static has no 'this' new object[] { // value to convert from pSource.GetValue(source, null) } // source property on // source instance ) ); // SetValue(...) } } } } } 

dstVar是我的目标实例。 pDest是目标实例中的当前PropertyInfo。

source是我的源实例。 pSource是源实例中的当前PropertyInfo。

用于我的目标属性的类型具有源属性类型的显式转换,这不需要任何工作

我需要像Ted H这样的function,但我实现了这样:

 var cast = typeof(dest).GetMethod("op_Explicit", new Type[] { typeof(source) }); var result = cast.Invoke(null, new object[] {value}); 

编辑:我最近需要一个更加进化的版本,这就是我想出来的。 请注意,它并未涵盖所有可用的转化。

 private static object DynamicCast(object source, Type destType) { Type srcType = source.GetType(); if (srcType == destType) return source; var paramTypes = new Type[] { srcType }; MethodInfo cast = destType.GetMethod("op_Implicit", paramTypes); if (cast == null) { cast = destType.GetMethod("op_Explicit", paramTypes); } if (cast != null) return cast.Invoke(null, new object[] { source }); if (destType.IsEnum) return Enum.ToObject(destType, source); throw new InvalidCastException(); } 

以Herman的答案为基础 ……我意识到源类和目标类都可以定义转换运算符。 所以这是我的版本:

 private static bool DynamicCast(object source, Type destType, out object result) { Type srcType = source.GetType(); if (srcType == destType) { result = source; return true; } result = null; BindingFlags bf = BindingFlags.Static | BindingFlags.Public; MethodInfo castOperator = destType.GetMethods(bf) .Union(srcType.GetMethods(bf)) .Where(mi => mi.Name == "op_Explicit" || mi.Name == "op_Implicit") .Where(mi => { var pars = mi.GetParameters(); return pars.Length == 1 && pars[0].ParameterType == srcType; }) .Where(mi => mi.ReturnType == destType) .FirstOrDefault(); if (castOperator != null) result = castOperator.Invoke(null, new object[] { source }); else return false; return true; } 

典型用法:

 object a = new A(); object o; if (DynamicCast(a, typeof(B), out o)) { B b = (B)o; ... } 

请注意以下事项:

  • 如果在源和目标中都定义了转换,则目标转换运算符方法优先
  • 该函数返回一个指示成功/失败的bool,以及一个out变量中的实际转换值(类似于TryParse方法)

谢谢以上所有人,我需要一个良好的开端。 我借了也加了。 在我的情况下,我需要以上所有内容,并且还需要搜索源和目标类型的所有祖先基类型,以查看它们中是否包含对目标类型的隐式或显式转换。 添加这个额外的要求我产生了以下。

  private static bool TryCast(object source, Type destType, out object result) { Type srcType = source.GetType(); if (srcType == destType) { result = source; return true; } MethodInfo cast = null; while (cast == null && srcType != typeof(object)) { cast = GetCastMethod(srcType, srcType, destType); if (cast == null) cast = GetCastMethod(destType, srcType, destType); srcType = srcType.BaseType; } if (cast != null) { result = cast.Invoke(null, new object[] { source }); return true; } if (destType.IsEnum) { result = Enum.ToObject(destType, source); return true; } result = null; return false; } private static MethodInfo GetCastMethod(Type typeWithMethod, Type srcType, Type destType) { while (typeWithMethod != typeof(object)) { foreach (MethodInfo method in typeWithMethod.GetMethods(BindingFlags.Static | BindingFlags.Public)) { if (method.ReturnType == destType && (method.Name == "op_Explicit" || method.Name == "op_Implicit")) { ParameterInfo[] parms = method.GetParameters(); if (parms != null && parms.Length == 1 && parms[0].ParameterType == srcType) return method; } } typeWithMethod = typeWithMethod.BaseType; } return null; }