2011-09-08 48 views
10

我想使用反射,並使用反射來執行隱式或顯式覆蓋。如何從反射執行顯式操作投射?

鑑於我已經定義美孚這樣

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 

。我收到以下異常。

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) 

這裏是我用來設置與反思

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 

我怎樣才能做到這一點的轉換屬性的代碼?

+0

如果你想做到這一點動態的,而不是代碼中投,馬克做了一件非常接近你在找什麼在使用表達式MSDN論壇(閱讀整個討論,不只是答案)。請參閱:http://social.msdn.microsoft.com/Forums/eu/csharplanguage/thread/c2a77a57-ebbb-4ac1-94c9-5287f01105ff – vcsjones

回答

5

那麼它真的沒有從非反射代碼不同,你仍然需要明確地投下數到Foo

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

活生生的例子:http://rextester.com/rundotnet?code=BPQ74480

出於興趣我試過幾個備擇方案。

  1. 使它成爲implicit投在Foo - 不工作,同樣的錯誤Live
  2. 使用Convert.ChangeType(17.17m,typeof(Foo)) - 也不起作用。 Live
+0

這是一個很好的博客文章,更多地討論這個拳擊和拆箱問題。 http://philosopherdeveloper.wordpress.com/2010/05/05/the-difference-between-converting-and-unboxing-read-my-mind-compiler/ –

4

我今天看了一下這個問題,而試圖通過名稱對象之間複製字段。看到選定的答案是「你只能明確地調用一個明確的操作符」,我感到非常失望。畢竟,任何事情都可以通過反思來完成。

我的問題是由於複雜類型,試圖在兩個類之間進行深層複製的反射方法。我試圖定義一個explicit operator轉換,但它似乎沒有被調用,所以我想出了一種通過反思來獲得它的方法。使用一些關於調用靜態方法的其他研究,我發現這適用於將存儲在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。

用於我的目標屬性的類型有從源的屬性類型進行顯式轉換,這種工作不需要任何

4

我需要的功能比如Ted H,但我實現它是這樣的:

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(); 

} 
+0

請注意,源和目標類型都可能允許轉換運營商。因此,例如,如果你有從A到B的明確轉換,那麼這可以在A類或B類或兩者中定義。 –

+0

我想我可能已經像這樣有意地實現了它。也許這樣它就可以匹配它在靜態C#代碼中的工作方式,但我不確定這一點。如果你真的想做大量的轉換,你可能也想看看'Convert'類和'TypeConverter'。 – Herman

1

建立在Herman's answer ......我意識到源類和目標類都可以定義轉換運算符。因此,這裏是我的版本:

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指示成功/失敗,並在實際轉換後的值變量(類似於TryParse方法)
0

非常感謝上面所提到的我需要的一個很好的開始。我借了,也加了。在我的情況下,我需要上述所有內容,還需要搜索源和目標類型的所有祖先基類型,以查看它們中的任何一個是否包含對我的目標類型的隱式或顯式轉換。添加這個額外的要求,我產生了下面。

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; 
    }