‘casting’ con la riflessione

Si consideri il seguente codice di esempio:

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

Ora ho bisogno di fare qualcosa di simile attraverso la riflessione:

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

Si noti che non posso assumere che PropertyInfo rappresenti sempre un valore long, né che quel valore sia sempre un decimale. Tuttavia, so che il valore può essere convertito nel tipo corretto per quella proprietà.

Come posso convertire il parametro ‘value’ nel tipo rappresentato dall’istanza PropertyInfo tramite reflection?

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

La risposta di Thomas ha ragione, ma ho pensato di aggiungere la mia constatazione che Convert.ChangeType non gestisce la conversione in tipi nullable. Per gestire i tipi nullable, ho usato il seguente codice:

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

Questo codice utilizza il seguente metodo di estensione:

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

La risposta di Thomas funziona solo per i tipi che implementano l’interfaccia IConvertible:

Affinché la conversione abbia esito positivo, il valore deve implementare l’interfaccia IConvertible, in quanto il metodo racchiude semplicemente una chiamata a un metodo IConvertible appropriato. Il metodo richiede che la conversione del valore in conversionType sia supportata.

Questo codice compila un’espressione linq che esegue l’unboxing (se necessario) e la conversione:

  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’espressione lambda risultante è uguale a (TOut) (TIn) Data in cui TIn è il tipo dei dati originali e TOut è il tipo dato

Contribuendo alla risposta di jeroenh, vorrei aggiungere che Convert.ChangeType si arresta in modo anomalo con un valore nullo, quindi la riga per ottenere il valore convertito dovrebbe essere:

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

Quando il Tipo è una Guida Nullable, nessuna delle soluzioni proposte sopra funziona. Il cast non valido da ” System.DBNull ” a ” System.Guid ” viene generato a Convert.ChangeType

Per correggere questa modifica:

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