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?


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

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!


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


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

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 { ... }

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

5

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

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

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

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...

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

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

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

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())
Петър Петров
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.