Frage

Ich möchte Reflexion verwenden und entweder eine implizite oder explizite Deckung mit Reflexion durchführen.

Vorausgesetzt, ich habe Foo auf diese Weise definiert

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

Wenn ich diesen Code ausführe

decimal someNumber = 42.42m;

var test = (Foo)someNumber;

Console.WriteLine(test);        // Writes 42.42 No problems

Wenn ich versuche, eine Klasse mit Foo als Mitgliedstyp zu definieren und Reflection zu verwenden, um sie festzulegen. Ich bekomme die folgende Ausnahme.

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)

Hier ist der Code, den ich benutze, um die Eigenschaft mit Reflexion festzulegen

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

Wie kann ich diese Konvertierung durchführen?

War es hilfreich?

Lösung

Nun, es ist wirklich nicht anders als bei Ihrem Nichtreflexionscode, Sie müssen die Nummer immer noch explizit an a geben Foo:

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

Live -Beispiel: http://rextester.com/rundotnet?code=bpq74480

Aus Interesse habe ich ein paar Alternativen ausprobiert.

  1. Mach es an implicit in Foo - funktioniert nicht, der gleiche Fehler Live
  2. Verwenden Convert.ChangeType(17.17m,typeof(Foo)) - funktioniert auch nicht. Live

Andere Tipps

Ich habe mir diese Frage heute angesehen, während ich versucht habe, Felder mit Namen zwischen Objekten zu kopieren. Ich war ziemlich enttäuscht zu sehen, dass die ausgewählte Antwort lautete: "Sie können nur explizit einen expliziten Bediener anrufen." Schließlich kann alles andere durch Reflexion getan werden.

Mein Problem war eine Reflexionsmethode, die versuchte, aufgrund eines komplexen Typs eine tiefe Kopie zwischen zwei Klassen zu erstellen. Ich habe versucht, eine zu definieren explicit operator Konvertierung, aber es schien nicht angerufen zu werden, also habe ich einen Weg gefunden, es durch Reflexion zu bekommen. Unter Verwendung einiger anderer Untersuchungen zum Aufrufen von statischen Methoden fand ich, dass dies für mich funktioniert, als ich einen komplexen Typ kopiert, der in PSOURCE in einen anderen Typ in Eigenschaften gepdest gespeichert ist. Der Typ in Pdest hat eine Konvertierung vom Typ 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 ist meine Zielinstanz. pdest ist die aktuelle Eigenschaft in der Zielinstanz.

Quelle ist meine Quellinstanz. PSOURCE ist die aktuelle Eigenschaft in der Quellinstanz.

Der Typ, der für meine Zieleigenschaft verwendet wird

Ich brauchte Funktionen wie Ted H, aber ich habe sie so implementiert:

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

Bearbeiten: Ich brauchte in letzter Zeit eine weiter entwickelte Version, und das habe ich mir ausgedacht. Beachten Sie, dass es nicht alle verfügbaren Conversions abdeckt.

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

}

Aufbauend auf Hermans Antwort... Ich erkannte, dass sowohl die Quell- als auch die Zielklasse den Conversion -Operator definieren können. Also hier ist meine Version:

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

Typische Verwendung:

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

Beachte das Folgende:

  • Wenn die Konvertierung sowohl in der Quelle als auch im Ziel definiert ist
  • Die Funktion gibt einen Bool zurück, der Erfolg/Misserfolg angibt, und der tatsächliche konvertierte Wert in einem out Variable (ähnlich wie bei TryParse -Methoden)

Vielen Dank oben für einen tollen Start in das, was ich brauchte. Ich habe mich geliehen und fügte hinzu. In meiner Situation brauchte ich alle oben genannten und musste auch alle Basistypen für Vorfahren nach Quell- und Zieltyp durchsuchen, um festzustellen, ob eine von ihnen eine implizite oder explizite Konvertierung zu meinen Zieltypen enthielt. Hinzufügen dieser zusätzlichen Anforderung, die ich unten erstellt habe.

    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;
    }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top