Question

Je veux utiliser la réflexion et faire soit un coversion implicite ou explicite en utilisant la réflexion.

Étant donné que je l'ai défini Foo cette façon

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

Quand je lance ce code

decimal someNumber = 42.42m;

var test = (Foo)someNumber;

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

Lorsque je tente de définir une classe Foo comme un type de membre et de réflexion d'utilisation pour le définir. Je reçois l'exception suivante.

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)

Voici le code que je utilise pour définir la propriété avec la réflexion

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

Comment puis-je faire cette conversion?

Était-ce utile?

La solution

Eh bien il est vraiment pas différent de votre code non-réflexion, vous devez toujours explicitement jeter le numéro à un Foo:

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

Un exemple concret: http://rextester.com/rundotnet?code=BPQ74480

Hors d'intérêt j'ai essayé quelques solutions de rechange.

  1. Faites un casting de implicit dans Foo - travail ne marche pas, même erreur en direct
  2. Utilisez Convert.ChangeType(17.17m,typeof(Foo)) - travail aussi ne marche pas. en direct

Autres conseils

Je regarde cette question aujourd'hui en essayant de champs de copie par nom entre les objets. J'ai été très déçu de voir la réponse choisie était « vous ne pouvez appeler explicitement un opérateur explicite. » Après tout, rien d'autre ne peut être fait par réflexion.

Mon problème était une méthode de réflexion en essayant de faire une copie en profondeur entre deux classes en raison d'un type complexe. J'ai essayé de définir une conversion explicit operator, mais il ne semble pas obtenir appelé, donc je me suis trouvé un moyen de l'obtenir par la réflexion. L'utilisation d'autres recherches sur l'appel de méthodes statiques, je trouve que cela fonctionne pour moi lors de la copie d'un type complexe stocké dans Psource dans un autre type de propriété pDEST. le type de pDEST a une conversion du type de 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 est mon instance de destination. pDEST est le PropertyInfo courant dans l'instance de destination.

source est mon instance source. Psource est le PropertyInfo courant dans l'instance source.

Le type utilisé pour ma propriété de destination a une conversion explicite du type de propriété source, cela fonctionne sans avoir besoin de

je avais besoin des fonctionnalités comme Ted H, mais je mis en œuvre comme ceci:

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

Modifier Je avais besoin d'une version plus évolué récemment, ce qui est ce que je suis venu avec. Sachez qu'il ne couvre pas toutes les conversions disponibles.

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

}

Miser sur réponse Herman ... j'ai réalisé que la source et la classe de destination peut définir l'opérateur de conversion. Donc, voici ma 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;
}

Utilisation typique:

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

Notez les points suivants:

  • Si la conversion est définie à la fois source et la destination, la méthode de l'opérateur de conversion de destination est la priorité donnée
  • La fonction retourne un bool indiquant le succès / échec, et la valeur convertie réelle à une variable d'out (similaire aux méthodes TryParse)

Merci à tous ci-dessus pour bien commencer ce que je avais besoin. Je l'ai emprunté et a également ajouté. Dans ma situation, il me fallait tout ce qui précède et aussi besoin de rechercher tous les types de base des ancêtres à la fois la source et le type de destination pour voir si l'un d'eux contenait une conversion implicite ou explicite à mes types de destination. L'ajout de cette exigence supplémentaire que je produisais le ci-dessous.

    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;
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top