2009-07-06 9 views
251

Vorrei impostare una proprietà di un oggetto tramite Reflection, con un valore di tipo string. Quindi, per esempio, supponiamo di avere una classe Ship, con una proprietà di Latitude, che è un double.Impostazione di una proprietà per riflessione con un valore stringa

Ecco quello che mi piacerebbe fare:

Ship ship = new Ship(); 
string value = "5.5"; 
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude"); 
propertyInfo.SetValue(ship, value, null); 

Come è, questo getta un ArgumentException:

oggetto di tipo 'System.String' non può essere convertito nel tipo 'sistema. Doppio'.

Come posso convertire il valore nel tipo corretto, in base a propertyInfo?

+1

Domanda per voi: è questa parte di una soluzione ORM personalizzata? – user3308043

risposta

419

È possibile utilizzare Convert.ChangeType() - Consente di utilizzare le informazioni di runtime su qualsiasi tipo IConvertible per modificare i formati di rappresentazione. Tuttavia, non tutte le conversioni sono possibili e potrebbe essere necessario scrivere una logica caso speciale se si desidera supportare conversioni da tipi che non sono IConvertible.

Il codice corrispondente (senza gestione delle eccezioni o la logica speciale caso) sarebbe:

Ship ship = new Ship(); 
string value = "5.5"; 
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude"); 
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null); 
31

Come molti altri hanno detto, che si desidera utilizzare Convert.ChangeType:

propertyInfo.SetValue(ship, 
    Convert.ChangeType(value, propertyInfo.PropertyType), 
    null); 

In realtà, io consiglio di guardare l'intero Convert Class.

Questa classe e molte altre classi utili fanno parte di System Namespace. Trovo utile scansionare quel namespace ogni anno o giù di lì per vedere quali caratteristiche mi sono perso. Provaci!

+1

L'OP vuole probabilmente la risposta generale, per l'impostazione di una proprietà di qualsiasi tipo che ha una conversione evidente da una stringa. –

+0

Buon punto. Modificherò e indicherò i veri risponditori, o cancellerò il mio se qualcuno aggiungerà quello che ho detto sul resto dello spazio dei nomi. –

3

Oppure si potrebbe provare:

propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null); 

//But this will cause problems if your string value IsNullOrEmplty... 
6

Probabilmente stai cercando per il metodo Convert.ChangeType. Per esempio:

Ship ship = new Ship(); 
string value = "5.5"; 
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude"); 
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null); 
5

Utilizzando Convert.ChangeType e ottenere il tipo di convertire dal PropertyInfo.PropertyType.

propertyInfo.SetValue(ship, 
         Convert.ChangeType(value, propertyInfo.PropertyType), 
         null); 
-6

Stai cercando di giocare con Reflection o stai cercando di creare un software di produzione? Mi chiederei perché stai usando la riflessione per impostare una proprietà.

Double new_latitude; 

Double.TryParse (value, out new_latitude); 
ship.Latitude = new_latitude; 
+0

Dovresti rispettare ciò che le persone tentano di fare e non ciò che pensi che debbano fare. Downvoted. (Da 'GenericProgramming.exe: ReflectionBenefits()') –

18

ho notato un sacco di persone stanno suggerendo Convert.ChangeType - Questo funziona per alcuni casi, tuttavia, non appena si inizia a coinvolgere nullable tipi si inizierà a ricevere InvalidCastExceptions:

http://weblogs.asp.net/pjohnson/archive/2006/02/07/Convert.ChangeType-doesn_2700_t-handle-nullables.aspx

Un wrapper è stato scritto qualche anno fa per gestire questo, ma non è nemmeno perfetto.

http://weblogs.asp.net/pjohnson/archive/2006/02/07/Convert.ChangeType-doesn_2700_t-handle-nullables.aspx

11

È possibile utilizzare un convertitore di tipi (senza il controllo degli errori):

Ship ship = new Ship(); 
string value = "5.5"; 
var property = ship.GetType().GetProperty("Latitude"); 
var convertedValue = property.Converter.ConvertFrom(value); 
property.SetValue(self, convertedValue); 

In termini di organizzazione del codice, è possibile creare un kind-of mixin che si tradurrebbe in codice come this:

Ship ship = new Ship(); 
ship.SetPropertyAsString("Latitude", "5.5"); 

Questo sarebbe ach ieved con questo codice:

public interface MPropertyAsStringSettable { } 
public static class PropertyAsStringSettable { 
    public static void SetPropertyAsString(
    this MPropertyAsStringSettable self, string propertyName, string value) { 
    var property = TypeDescriptor.GetProperties(self)[propertyName]; 
    var convertedValue = property.Converter.ConvertFrom(value); 
    property.SetValue(self, convertedValue); 
    } 
} 

public class Ship : MPropertyAsStringSettable { 
    public double Latitude { get; set; } 
    // ... 
} 

MPropertyAsStringSettable può essere riutilizzato per molte classi diverse.

È inoltre possibile creare il proprio personalizzato type converters di allegare alle proprietà o classi:

public class Ship : MPropertyAsStringSettable { 
    public Latitude Latitude { get; set; } 
    // ... 
} 

[TypeConverter(typeof(LatitudeConverter))] 
public class Latitude { ... } 
+0

C'è qualche ragione particolare per cui hai aggiunto l'interfaccia marcatore invece di usare semplicemente 'oggetto'? – Groo

+0

Sì, l'interfaccia marcatore funge da segnaposto per aggiungere i metodi di estensione a. L'uso di 'object' aggiungerebbe i metodi di estensione a tutte le classi, cosa che generalmente non è desiderabile. –

2

Se si sta scrivendo Metro app, è necessario utilizzare altro codice:

Ship ship = new Ship(); 
string value = "5.5"; 
PropertyInfo propertyInfo = ship.GetType().GetTypeInfo().GetDeclaredProperty("Latitude"); 
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType)); 

Nota:

ship.GetType().GetTypeInfo().GetDeclaredProperty("Latitude"); 

anziché

3

Ho provato la risposta da LBushkin e ha funzionato benissimo, ma non funzionerà con valori nulli e campi nullable. Quindi l'ho modificato in questo modo:

propertyName= "Latitude"; 
PropertyInfo propertyInfo = ship.GetType().GetProperty(propertyName); 
if (propertyInfo != null) 
{ 
    Type t = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType; 
    object safeValue = (value == null) ? null : Convert.ChangeType(value, t); 
    propertyInfo.SetValue(ship, safeValue, null); 
}