Festlegen einer Eigenschaft durch Reflektion mit einem Zeichenfolgenwert


312

Ich möchte eine Eigenschaft eines Objekts über Reflection mit einem Wert vom Typ festlegen string. Nehmen wir zum Beispiel an, ich habe eine ShipKlasse mit der Eigenschaft Latitudea double.

Folgendes möchte ich tun:

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

Wie es ist, wirft dies ein ArgumentException:

Objekt vom Typ 'System.String' kann nicht in Typ 'System.Double' konvertiert werden.

Wie kann ich den Wert basierend auf dem richtigen Typ konvertieren propertyInfo?


1
Frage an Sie: Ist dies Teil einer benutzerdefinierten ORM-Lösung?
user3308043

Antworten:


527

Sie können verwenden Convert.ChangeType()- Sie können Laufzeitinformationen für jeden IConvertibleTyp verwenden, um Darstellungsformate zu ändern. Es sind jedoch nicht alle Konvertierungen möglich, und Sie müssen möglicherweise eine Sonderfalllogik schreiben, wenn Sie Konvertierungen von Typen unterstützen möchten, die dies nicht sind IConvertible.

Der entsprechende Code (ohne Ausnahmebehandlung oder Sonderfalllogik) wäre:

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

Überprüfen Sie die Antwort von @AliKaraca unten. Sowohl dies als auch das folgende sind schnell und locker, erledigen aber die Arbeit für gängige Typen.
Aaron Hudon

Gibt es ein TryChangeTypeoder CanChangeType?
Shimmy Weitzhandler

34

Wie mehrere andere gesagt haben, möchten Sie Folgendes verwenden Convert.ChangeType:

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

Tatsächlich empfehle ich Ihnen, sich die gesamte ConvertKlasse anzusehen .

Diese Klasse und viele andere nützliche Klassen sind Teil des SystemNamespace . Ich finde es nützlich, diesen Namespace jedes Jahr oder so zu scannen, um zu sehen, welche Funktionen ich verpasst habe. Versuche es!


1
Das OP möchte wahrscheinlich die allgemeine Antwort für das Festlegen einer Eigenschaft eines beliebigen Typs, der eine offensichtliche Konvertierung von einer Zeichenfolge aufweist.
Daniel Earwicker

Guter Punkt. Ich werde meine Antworten bearbeiten und auf sie verweisen oder meine löschen, wenn jemand das hinzufügt, was ich über den Rest des Namespace gesagt habe.
John Saunders

19

Ich stelle fest, dass viele Leute empfehlen Convert.ChangeType- Dies funktioniert in einigen Fällen, aber sobald Sie anfangen, nullableTypen einzubeziehen, erhalten Sie InvalidCastExceptions:

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

Ein Wrapper wurde vor einigen Jahren geschrieben, um damit umzugehen, aber das ist auch nicht perfekt.

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


13

Ich habe die Antwort von LBushkin ausprobiert und es hat großartig funktioniert, aber es funktioniert nicht für Nullwerte und nullfähige Felder. Also habe ich es so geändert:

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

Ich muss mich bedanken, als ich diesen Fall traf und dies ist die einzige Lösung. danke ~!
Franva

11

Sie können einen Typkonverter verwenden (keine Fehlerprüfung):

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 Bezug auf die Organisation des Codes könnten Sie eine Art Mixin erstellen , das zu Code wie dem folgenden führt:

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

Dies würde mit diesem Code erreicht werden:

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 kann für viele verschiedene Klassen wiederverwendet werden.

Sie können auch eigene benutzerdefinierte Typkonverter erstellen , die an Ihre Eigenschaften oder Klassen angehängt werden:

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

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

Gibt es einen bestimmten Grund, warum Sie das Marker-Insterface hinzugefügt haben, anstatt nur zu verwenden object?
Groo

1
Ja, die Markierungsschnittstelle dient als Platzhalter zum Hinzufügen der Erweiterungsmethoden. Die Verwendung objectwürde die Erweiterungsmethoden allen Klassen hinzufügen, was im Allgemeinen nicht wünschenswert ist.
Jordão

6

Sie suchen wahrscheinlich nach der Convert.ChangeTypeMethode. Zum Beispiel:

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

5

Verwenden Convert.ChangeTypeund Abrufen des zu konvertierenden Typs aus dem PropertyInfo.PropertyType.

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

4

Ich werde dies mit einer allgemeinen Antwort beantworten. Normalerweise funktionieren diese Antworten nicht mit Anleitungen. Hier ist auch eine funktionierende Version mit Anleitungen.

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
Dies sollte die akzeptierte Antwort sein. Es funktioniert auch mit GUIDs <3. Danke, Ali (das ist der Spitzname meiner Tochter)
Cătălin Rădoi

3

Oder Sie könnten versuchen:

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

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

2

Wenn Sie eine Metro-App schreiben, sollten Sie anderen Code verwenden:

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

Hinweis:

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

anstatt

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

0

Die Verwendung des folgenden Codes sollte Ihr Problem lösen:

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

-9

Möchten Sie mit Reflection herumspielen oder eine Produktionssoftware erstellen? Ich würde fragen, warum Sie Reflektion verwenden, um eine Eigenschaft festzulegen.

Double new_latitude;

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

1
Sie sollten respektieren, was die Leute versuchen und nicht, was Sie denken, dass sie tun müssen. Abgestimmt. (Von GenericProgramming.exe:ReflectionBenefits())
30етър Петров
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.