2009-09-09 6 views
68

Considérons le code exemple suivant:« casting » avec la réflexion

class SampleClass 
{ 
    public long SomeProperty { get; set; } 
} 

public void SetValue(SampleClass instance, decimal value) 
{ 
    // value is of type decimal, but is in reality a natural number => cast 
    instance.SomeProperty = (long)value; 
} 

Maintenant, je dois faire quelque chose de similaire par la réflexion:

void SetValue(PropertyInfo info, object instance, object value) 
{ 
    // throws System.ArgumentException: Decimal can not be converted to Int64 
    info.SetValue(instance, value) 
} 

Notez que je ne peux pas supposer que le PropertyInfo représente toujours une longue , ni cette valeur est toujours une décimale. Cependant, je sais que la valeur peut être castée au bon type pour cette propriété.

Comment puis-je convertir le paramètre 'value' en type représenté par PropertyInfo par réflexion?

Répondre

114
void SetValue(PropertyInfo info, object instance, object value) 
{ 
    info.SetValue(instance, Convert.ChangeType(value, info.PropertyType)); 
} 
35

La réponse de Thomas est juste, mais je pensais que je voudrais ajouter ma conclusion que Convert.ChangeType ne gère pas la conversion à des types nullable. Pour gérer les types nullable, je le code suivant:

void SetValue(PropertyInfo info, object instance, object value) 
{ 
    var targetType = info.PropertyType.IsNullableType() 
     ? Nullable.GetUnderlyingType(info.PropertyType) 
     : info.PropertyType; 
    var convertedValue = Convert.ChangeType(value, targetType); 

    info.SetValue(instance, convertedValue, null); 
} 

Ce code utilise la méthode d'extension suivante:

public static class TypeExtensions 
{ 
    public static bool IsNullableType(this Type type) 
    { 
    return type.IsGenericType 
    && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)); 
    } 
+0

cela a fonctionné pour moi, explication très claire, merci –

9

Contribuer à la réponse de jeroenh, je voudrais ajouter que les accidents de Convert.ChangeType avec valeur nulle, de sorte que la ligne pour obtenir la valeur convertie doit être:

var convertedValue = value == null ? null : Convert.ChangeType(value, targetType); 
+0

J'étais sur le point de poster ceci! –

1

Lorsque le type est un Guid Nullable alors d'aucun des travaux de solutions proposées ci-dessus. non valide cast de 'System.DBNull' à 'System.Guid' exception est levée à Convert.ChangeType

Pour résoudre ce changement:

var convertedValue = value == System.DBNull.Value ? null : Convert.ChangeType(value, targetType); 
+1

Ce problème n'est pas spécifique à Guid, mais plutôt au fait que vous obtenez 'DBNull.Value' au lieu de simplement' null' lorsque vous récupérez des valeurs nulles de la base de données via ADO.Net. Vous verrez la même chose avec nullable, par exemple. – jeroenh

28

Thomas répond ne fonctionne que pour les types qui mettent en œuvre l'interface IConvertible:

Pour que la conversion réussisse, value doit implémenter l'interface IConvertible, car la méthode encapsule simplement un appel vers une méthode IConvertible appropriée. La méthode nécessite que la conversion de la valeur en conversionType soit prise en charge.

Ce code Dressez une expression LINQ qui fait le unboxing (si nécessaire) et la conversion:

public static object Cast(this Type Type, object data) 
    { 
     var DataParam = Expression.Parameter(typeof(object), "data"); 
     var Body = Expression.Block(Expression.Convert(Expression.Convert(DataParam, data.GetType()), Type)); 

     var Run = Expression.Lambda(Body, DataParam).Compile(); 
     var ret = Run.DynamicInvoke(data); 
     return ret; 
    } 

L'expression lambda résultant est égal à (TouT) (TIN) Les données où TIN est le type de les données d'origine et TOut est le type donné

+1

C'est en fait la réponse que je suis venu chercher. Coulée dynamique non-IConvertible. – jnm2

+0

Vous pourriez le marquer comme accepté :) – rafael

+1

Heh je voudrais- si j'étais OP. – jnm2