Setting a property by reflection with a string value
Asked Answered
A

12

367

I'd like to set a property of an object through Reflection, with a value of type string. So, for instance, suppose I have a Ship class, with a property of Latitude, which is a double.

Here's what I'd like to do:

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

As is, this throws an ArgumentException:

Object of type 'System.String' cannot be converted to type 'System.Double'.

How can I convert value to the proper type, based on propertyInfo?

Antiphonary answered 6/7, 2009 at 20:43 Comment(1)
Question for you: is this part of a custom ORM solution?Rosalvarosalyn
W
597

You can use Convert.ChangeType() - It allows you to use runtime information on any IConvertible type to change representation formats. Not all conversions are possible, though, and you may need to write special case logic if you want to support conversions from types that are not IConvertible.

The corresponding code (without exception handling or special case logic) would be:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
Webby answered 6/7, 2009 at 20:44 Comment(2)
Review @AliKaraca answer below. Both this and the one below are fast and loose but do the job for common types.Lichen
Is there a TryChangeType or CanChangeType?Braque
D
37

As several others have said, you want to use Convert.ChangeType:

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

In fact, I recommend you look at the entire Convert Class.

This class, and many other useful classes are part of the System Namespace. I find it useful to scan that namespace every year or so to see what features I've missed. Give it a try!

Diatomic answered 6/7, 2009 at 20:44 Comment(2)
The OP probably wants the general answer, for setting a property of any type that has an obvious conversion from a string.Kurr
Good point. I'll edit and point to the real answerers, or will delete mine if someone will add what I said about the rest of the namespace.Diatomic
L
31

I tried the answer from LBushkin and it worked great, but it won't work for null values and nullable fields. So I've changed it to this:

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);
}
Licko answered 4/1, 2017 at 14:51 Comment(1)
I have to say thank you as I met this case and this is the only one solution. thanks~!Mane
C
20

I notice a lot of people are recommending Convert.ChangeType - This does work for some cases however as soon as you start involving nullable types you will start receiving InvalidCastExceptions:

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

A wrapper was written a few years ago to handle this but that isn't perfect either.

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

Codification answered 13/8, 2010 at 11:8 Comment(0)
P
12

You can use a type converter (no error checking):

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 terms of organizing the code, you could create a kind-of mixin that would result in code like this:

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

This would be achieved with this 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 can be reused for many different classes.

You can also create your own custom type converters to attach to your properties or classes:

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

[TypeConverter(typeof(LatitudeConverter))]
public class Latitude { ... }
Preselector answered 13/9, 2010 at 13:51 Comment(2)
Is there any particular reason why you added the marker insterface instead of just using object?Miniaturist
Yes, the marker interface serves as a placeholder to add the extension methods to. Using object would add the extension methods to all classes, which is not generally desirable.Jambeau
F
8

You're probably looking for the Convert.ChangeType method. For example:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
Feverish answered 6/7, 2009 at 20:48 Comment(0)
C
5

Using Convert.ChangeType and getting the type to convert from the PropertyInfo.PropertyType.

propertyInfo.SetValue( ship,
                       Convert.ChangeType( value, propertyInfo.PropertyType ),
                       null );
Costermansville answered 6/7, 2009 at 20:48 Comment(0)
A
5

I will answer this with a general answer. Usually these answers not working with guids. Here is a working version with guids too.

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); 
Ancy answered 30/5, 2018 at 8:35 Comment(2)
This should be the accepted answer. It also works with GUIDs <3. Thanks, Ali (that's my daughter's nickname)Boredom
But still seems not to work with nullable types.Chiastolite
F
3

Or you could try:

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

//But this will cause problems if your string value IsNullOrEmplty...
Flex answered 6/7, 2009 at 20:47 Comment(0)
C
2

If you are writing Metro app, you should use other code:

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

Note:

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

instead of

ship.GetType().GetProperty("Latitude");
Clouse answered 10/10, 2013 at 8:22 Comment(0)
A
1

Using the following code should solve your issue:

item.SetProperty(prop.Name, Convert.ChangeType(item.GetProperty(prop.Name).ToString().Trim(), prop.PropertyType));
Apish answered 3/6, 2019 at 10:16 Comment(0)
R
-11

Are you looking to play around with Reflection or are you looking to build a production piece of software? I would question why you're using reflection to set a property.

Double new_latitude;

Double.TryParse (value, out new_latitude);
ship.Latitude = new_latitude;
Rabaul answered 6/7, 2009 at 20:57 Comment(2)
You should respect what people attempt to do and not what you think they must do. Downvoted. (From GenericProgramming.exe:ReflectionBenefits() )Gothicize
Er, perhaps because you don't know what the Property is beforehand, and while it's typed the value you're using is always a string? This is my case: I'm screen scraping HTML, so the value I get is always a string, and which properties I want and how to find them are defined in a config file, so Reflection is the only reasonable way to do it.Chiastolite

© 2022 - 2024 — McMap. All rights reserved.