Impostazione di una proprietà tramite riflesso con un valore stringa


312

Vorrei impostare una proprietà di un oggetto tramite Reflection, con un valore di tipo string. Quindi, per esempio, supponiamo che io abbia una Shipclasse, con una proprietà di Latitude, che è a double.

Ecco cosa mi piacerebbe fare:

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

Come è, questo genera un ArgumentException:

L'oggetto di tipo 'System.String' non può essere convertito nel tipo 'System.Double'.

Come posso convertire il valore nel tipo corretto, basato su propertyInfo?


1
Domanda per te: questa parte di una soluzione ORM personalizzata?
user3308043

Risposte:


527

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

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

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

Rivedi la risposta @AliKaraca di seguito. Sia questo che quello sotto sono veloci e sciolti ma fanno il lavoro per tipi comuni.
Aaron Hudon,

C'è un TryChangeTypeo CanChangeType?
Shimmy Weitzhandler il

34

Come molti altri hanno già detto, vuoi usare Convert.ChangeType:

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

In effetti, ti consiglio di guardare l'intera Convertclasse .

Questa classe e molte altre utili classi fanno parte dello Systemspazio dei nomi . Trovo utile scansionare quello spazio dei nomi ogni anno o giù di lì per vedere quali funzioni mi sono perso. Provaci!


1
L'OP probabilmente vuole la risposta generale, per impostare una proprietà di qualsiasi tipo che abbia una conversione evidente da una stringa.
Daniel Earwicker,

Buon punto. Modificherò e indicherò i veri risponditori, o eliminerò il mio se qualcuno aggiungerà ciò che ho detto sul resto dello spazio dei nomi.
John Saunders,

19

Ho notato che molte persone stanno raccomandando Convert.ChangeType- Questo funziona in alcuni casi, tuttavia non appena inizi a coinvolgere nullabletipi inizierai a ricevere InvalidCastExceptions:

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

Alcuni anni fa è stato scritto un wrapper per gestirlo, ma neanche questo è perfetto.

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


13

Ho provato la risposta di LBushkin e ha funzionato alla grande, ma non funzionerà con valori null e campi nullble. Quindi l'ho cambiato in questo:

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

Devo dire grazie quando ho incontrato questo caso e questa è l'unica soluzione. grazie ~!
Franva,

11

È possibile utilizzare un convertitore di tipi (nessun 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 tipo di mixin che si tradurrebbe in codice come questo:

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

Ciò si otterrebbe 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.

Puoi anche creare i tuoi convertitori di tipi personalizzati da allegare alle tue proprietà o classi:

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

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

Esiste un motivo particolare per cui hai aggiunto l'interfaccia del marcatore invece di utilizzarlo object?
Groo

1
Sì, l'interfaccia del marker funge da segnaposto a cui aggiungere i metodi di estensione. L'uso di objectaggiungerebbe i metodi di estensione a tutte le classi, il che non è generalmente desiderabile.
Jordão,

6

Probabilmente stai cercando il Convert.ChangeTypemetodo. 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

Utilizzare Convert.ChangeTypee ottenere il tipo da convertire da PropertyInfo.PropertyType.

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

4

Risponderò a questo con una risposta generale. Di solito queste risposte non funzionano con le guide. Ecco una versione funzionante anche con le guide.

var stringVal="6e3ba183-89d9-e611-80c2-00155dcfb231"; // guid value as string to set
var prop = obj.GetType().GetProperty("FooGuidProperty"); // property to be setted
var propType = prop.PropertyType;

// var will be type of guid here
var valWithRealType = TypeDescriptor.GetConverter(propType).ConvertFrom(stringVal); 

1
Questa dovrebbe essere la risposta accettata. Funziona anche con GUID <3. Grazie, Ali (questo è il soprannome di mia figlia)
Cătălin Rădoi,

3

Oppure potresti provare:

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

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

2

Se stai scrivendo l'app Metro, dovresti usare un 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");

invece di

ship.GetType().GetProperty("Latitude");

0

L'uso del codice seguente dovrebbe risolvere il problema:

item.SetProperty(prop.Name, Convert.ChangeType(item.GetProperty(prop.Name).ToString().Trim(), prop.PropertyType));

-9

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

Double new_latitude;

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

1
Dovresti rispettare ciò che la gente tenta di fare e non ciò che pensi che debbano fare. Downvoted. (Da GenericProgramming.exe:ReflectionBenefits())
Петър Петров
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.