Définition d'une propriété par réflexion avec une valeur de chaîne

312

Je voudrais définir une propriété d'un objet via Reflection, avec une valeur de type string. Donc, par exemple, supposons que j'ai une Shipclasse, avec une propriété de Latitude, qui est a double.

Voici ce que j'aimerais faire:

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

En l'état, cela lance un ArgumentException:

L'objet de type 'System.String' ne peut pas être converti en type 'System.Double'.

Comment puis-je convertir la valeur au type approprié, en fonction de propertyInfo?

David Hodgson
la source
1
Question pour vous: cela fait-il partie d'une solution ORM personnalisée?
user3308043

Réponses:

527

Vous pouvez utiliser Convert.ChangeType()- Il vous permet d'utiliser des informations d'exécution sur n'importe quel IConvertibletype pour modifier les formats de représentation. Cependant, toutes les conversions ne sont pas possibles et vous devrez peut-être écrire une logique de cas particulier si vous souhaitez prendre en charge les conversions de types qui ne le sont pas IConvertible.

Le code correspondant (sans gestion des exceptions ni logique de cas particulier) serait:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
LBushkin
la source
Consultez la réponse @AliKaraca ci-dessous. Ceci et celui ci-dessous sont rapides et lâches mais font le travail pour les types courants.
Aaron Hudon
Y a-t-il un TryChangeTypeou CanChangeType?
Shimmy Weitzhandler
34

Comme plusieurs autres l'ont dit, vous souhaitez utiliser Convert.ChangeType:

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

En fait, je vous recommande de regarder l'ensemble de la Convertclasse .

Cette classe et de nombreuses autres classes utiles font partie de l' Systemespace de noms . Je trouve utile de scanner cet espace de noms tous les ans environ pour voir quelles fonctionnalités j'ai manquées. Essaie!

John Saunders
la source
1
L'OP veut probablement la réponse générale, pour définir une propriété de tout type qui a une conversion évidente à partir d'une chaîne.
Daniel Earwicker
Bon point. Je vais modifier et pointer vers les vrais répondeurs, ou supprimer le mien si quelqu'un ajoute ce que j'ai dit sur le reste de l'espace de noms.
John Saunders
19

Je remarque que beaucoup de gens recommandent Convert.ChangeType- cela fonctionne dans certains cas, mais dès que vous commencez à impliquer des nullabletypes, vous commencerez à recevoir InvalidCastExceptions:

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

Un wrapper a été écrit il y a quelques années pour gérer cela, mais ce n'est pas parfait non plus.

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

Tablette
la source
13

J'ai essayé la réponse de LBushkin et cela a très bien fonctionné, mais cela ne fonctionnera pas pour les valeurs nulles et les champs nullables. Je l'ai donc changé en ceci:

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);
}
Ashkan Sirous
la source
Je dois vous remercier car j'ai rencontré ce cas et c'est la seule solution. merci ~!
Franva
11

Vous pouvez utiliser un convertisseur de type (pas de vérification d'erreur):

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

En termes d'organisation du code, vous pouvez créer une sorte de mixage qui aboutirait à un code comme celui-ci:

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

Ceci serait réalisé avec ce code:

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 peut être réutilisé pour de nombreuses classes différentes.

Vous pouvez également créer vos propres convertisseurs de types personnalisés à attacher à vos propriétés ou classes:

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

[TypeConverter(typeof(LatitudeConverter))]
public class Latitude { ... }
Jordão
la source
Y a-t-il une raison particulière pour laquelle vous avez ajouté l'interface de marqueur au lieu de simplement l'utiliser object?
Groo
1
Oui, l'interface de marqueur sert d'espace réservé pour ajouter les méthodes d'extension à. L'utilisation objectajouterait les méthodes d'extension à toutes les classes, ce qui n'est généralement pas souhaitable.
Jordão
6

Vous cherchez probablement la Convert.ChangeTypeméthode. Par exemple:

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

En utilisant Convert.ChangeType et obtention du type à convertir à partir du PropertyInfo.PropertyType.

propertyInfo.SetValue( ship,
                       Convert.ChangeType( value, propertyInfo.PropertyType ),
                       null );
tvanfosson
la source
4

Je vais y répondre par une réponse générale. Habituellement, ces réponses ne fonctionnent pas avec les guides. Voici aussi une version de travail avec des guides.

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); 
Ali Karaca
la source
1
Ce devrait être la réponse acceptée. Il fonctionne également avec des GUID <3. Merci, Ali (c'est le surnom de ma fille)
Cătălin Rădoi
3

Ou vous pouvez essayer:

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

//But this will cause problems if your string value IsNullOrEmplty...
bytebender
la source
2

Si vous écrivez l'application Metro, vous devez utiliser un autre code:

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

Remarque:

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

au lieu de

ship.GetType().GetProperty("Latitude");
Serhiy
la source
0

L'utilisation du code suivant devrait résoudre votre problème:

item.SetProperty(prop.Name, Convert.ChangeType(item.GetProperty(prop.Name).ToString().Trim(), prop.PropertyType));
Marco Sotto
la source
-9

Cherchez-vous à jouer avec Reflection ou cherchez-vous à créer un logiciel de production? Je me demande pourquoi vous utilisez la réflexion pour définir une propriété.

Double new_latitude;

Double.TryParse (value, out new_latitude);
ship.Latitude = new_latitude;
clemahieu
la source
1
Vous devez respecter ce que les gens tentent de faire et non ce que vous pensez qu'ils doivent faire. Voté. (De GenericProgramming.exe:ReflectionBenefits())
Петър Петров