2010-10-28 8 views
11

Ich habe einige Code (der gut arbeitet), die etwa wie folgt aussieht:Wie kann ich mit reflection von int in dezimal konvertieren?

 int integer = 42; 
     decimal? castTo = integer; 

Dann wollte ich etwas ähnliches mit Reflexion tun, mit einigen Code, der wie folgt aussieht:

object value = source; // source was an int originally 
var parameters = new object[1];  
    ... 
    parameters[0] = value; 
    var setMethod = property.GetSetMethod();  
    // Call the set method, which takes a decimal? as a parameter 
    setMethod.Invoke(o, parameters); 

Wenn Ich tue das, ich bekomme:

Warum würde eine implizite Typumwandlung, die anderswo gut funktioniert, mit Reflektion fehlschlagen? Gibt es einen Trick zur Verwendung der Reflektion, um diese Konvertierung durchzuführen?


bearbeiten: Vielen Dank an Alle für die Antworten. Hier ist die Lösung kam ich mit auf der Grundlage der Antworten:

private object Convert(object source, Type destinationType) 
    { 
     if (source == null) 
     { 
      return null; 
     } 

     var sourceType = source.GetType(); 

     // unwrap nullable types 
     var nullableType = Nullable.GetUnderlyingType(destinationType); 
     if(nullableType != null) 
     { 
      destinationType = nullableType; 
     } 

     nullableType = Nullable.GetUnderlyingType(sourceType); 
     if(nullableType != null) 
     { 
      sourceType = nullableType; 
     } 


     var implicitCastMethod = 
      destinationType.GetMethod("op_Implicit", 
           new[] { sourceType }); 

     if(implicitCastMethod == null) 
     { 
      return null; 
     } 

     return implicitCastMethod.Invoke(null, new[] { source }); 
    } 

Edit # 2: Ich wünschte, jemand System.Convert.ChangeType() erwähnt hatte, die diese Fälle behandelt und mehr. Es stellt sich heraus, dass op_Implicit nur zu weniger restriktiven numerischen Typen konvertieren kann. (natürlich, daher das "Implizite" im Namen). Mit anderen Worten arbeitete die erste Lösung für intdecimal? aber nicht decimal?int. (Es scheint, dass ich brauchen würde diesen Code zu ändern, auch op_Explicit versuchen, wenn die implizite Umwandlung fehlgeschlagen ist, wenn ich von decimal? zurück zu int eine Umwandlung zu handhaben zu können wollte.)

Da System.Convert.ChangeType() funktioniert nicht mit Nullable<> Typen, ich endete schließlich einen Code ähnlich wie mit, was ich here gefunden (leicht modifiziert):

private static object Convert(object source, Type destinationType) 
    { 
     if(destinationType == null) 
     { 
      throw new ArgumentNullException("destinationType"); 
     } 

     if(destinationType.IsGenericType && 
      destinationType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) 
     { 
      if (source == null) 
      { 
       return null; 
      } 
      destinationType = Nullable.GetUnderlyingType(destinationType);     
     } 

     return System.Convert.ChangeType(source, destinationType); 


    } 

Antwort

5

Sie die Umwandlung selbst, wie der Compiler behandelt die Besetzung in einer nicht-Reflexion Umwelt zu tun haben. Da der Reflection-Code grundsätzlich Typen und Objekte wie den Compiler auswertet, müssen Sie nach einer Methode namens op_implicit mit den benötigten Parametern (in Ihrem Fall Int32) auf Ihrem Objekt suchen und diese aufrufen. Danach können Sie den Eigenschaftenaccessor aufrufen. Ein möglicher Weg wäre die folgende:

//search for an implicit cast operator on the target type 
MethodInfo[] methods = targetType.GetMethods(); 
foreach(MethodInfo method = source.GetType().GetMethod("op_Implicit")) 
{ 
    if (method.Name == "op_Implicit") 
    { 
    ParameterInfo[] parameters = method.GetParameters(); 
    if (parameters.Length == 1 && parameters[0].ParameterType == value.GetType()) 
    { 
     value = method.Invoke(obj,new object[]{value}); 
     break; 
    } 
    } 
} 
+0

Dies ist im Wesentlichen, was für mich arbeitete. Zumindest hat es mich auf die richtige Spur gebracht. Vielen Dank! – mpontillo

+0

'MethodInfo method = source.GetType(). GetMethod (" op_Implicit ");' ist einfacher zu lesen als die explizit geloopte Suche. –

+0

@Merlyn, yeah, du wirst bemerken, dass ich das gemacht habe, wenn du meine erste Bearbeitung gelesen hast, außer dass ich den Typ auch in den GetMethod() - Aufruf eingefügt habe. Sonst, wenn es mehrere überladene op_Implicit-Methoden gäbe, müsste ich noch eine Schleife machen? – mpontillo

2

Why would an implicit type conversion that works fine elsewhere fail with reflection?

Da gibt es keine Konvertierung. Implizite Konvertierung bedeutet nicht, dass es automatisch passiert, wenn Sie es im Code verwenden, fügt der Compiler den Code dafür hinzu.

Wenn Sie es in Reflexion verwenden möchten, müssen Sie das gleiche tun, d. H. Die statische Methode finden, die die Konvertierung von einem Typ zum anderen durchführt, und sie aufrufen.

+0

Danke für die Erklärung. Ich glaube, ich hatte gehofft, dass die Reflexion einen einfacheren Weg dazu hätte, aber leider. – mpontillo

5

Die Laufzeitumgebung kennt keine impliziten Konvertierungen.

Sie können op_Implicit oder eine andere Konvertierungsmethode durch Reflektion aufrufen, aber auf diese Weise erhalten Sie nur die spezifische Konvertierungssemantik, die Sie implementieren. Wenn Sie C# 4.0 verwenden, würde ich den Typ "dynamic" hier empfehlen, da er die C# -Konversionssemantik automatisch implementiert.

+3

+1 für C# 4.0 'dynamische' Empfehlung –

+0

Danke dafür. Wir verwenden C# 3.5, aber ich sehe C# 4.0-Funktionen wie diese, die praktisch aussehen. – mpontillo

1

Andere Antworten haben bereits behandelt warum die implizite Konvertierung funktioniert nicht. Wenn Ihre Konvertierung implizit sein soll, verwenden Sie eine der anderen Antworten. Für den Fall, müssen Sie nicht tatsächlich die Konvertierung implizit sein, hier ist eine einfachere Option:

class Test 
{ 
    public decimal? Val { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
    object o = new Test(); 
    object source = 5; 
    var setMethod = typeof(Test).GetProperty("Val").GetSetMethod(); 
    // Just do the cast explicitly 
    setMethod.Invoke(o, new object[] { (decimal?)(int)source }); 
    } 
} 

Beachten Sie, dass, wenn Sie die (decimal?) Besetzung fehlt, Sie den Fehler, dass die ursprüngliche Frage zitiert. Wenn Sie die (int) Besetzung verpassen, erhalten Sie diesen Fehler:

+0

Neuartige Idee; Es brachte mich zum Lachen. Ich denke, ich hätte das in der Frage sagen sollen, aber das eigentliche Ziel war es, dies dynamisch zu machen; Mache ich nicht immer (dezimal?) (Int). =) – mpontillo

+0

@Mike: Ich dachte mir so viel, aber Sie können keine Erfahrung mit einer einzigen Frage über das Internet messen. Es wäre für einen Neuling nicht 100% offensichtlich, dass Sie * zweimal * casten müssen, um dies zum Laufen zu bringen. Ich werde diese Antwort hinterlassen, für den Fall, dass es für jemanden mit weniger Erfahrung nützlich ist, der immer noch denselben Fehler findet. –