2009-07-06 8 views
251

Я хотел бы установить свойство объекта через Reflection со значением типа string. Итак, предположим, что у меня есть класс Ship с свойством Latitude, который является double.Установка свойства путем отражения со строковым значением

Вот что я хотел бы сделать:

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

Как есть, это бросает ArgumentException:

Объект «System.String» типа не может быть преобразовано в тип «системы. Double».

Как преобразовать значение в нужный тип на основе propertyInfo?

+1

Вопрос для вас: эта часть пользовательского решения ORM? – user3308043

ответ

419

Вы можете использовать Convert.ChangeType() - Он позволяет вам использовать информацию о времени выполнения для любого типа IConvertible для изменения форматов представления. Однако не все преобразования возможны, и вам может потребоваться написать специальную логику case, если вы хотите поддерживать конверсии из типов, которые не являются IConvertible.

Соответствующий код (без обработки исключений или специального случай логики) будет:

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

Поскольку некоторые другие говорили, вы хотите использовать Convert.ChangeType:

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

На самом деле, я рекомендую вам посмотреть на весь Convert Class.

Этот класс и многие другие полезные классы являются частью System Namespace. Мне посчастливилось сканировать это пространство имен каждый год или около того, чтобы увидеть, какие функции я пропустил. Попробуй!

+1

OP, вероятно, хочет получить общий ответ для установки свойства любого типа, который имеет очевидное преобразование из строки. –

+0

Хорошая точка. Я отредактирую и укажу на реальных ответчиков, или удалю мой, если кто-то добавит то, что я сказал о остальной части пространства имен. –

3

Или вы могли бы попробовать:

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

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

Вы, вероятно, ищете способ Convert.ChangeType. Например:

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

Convert.ChangeType Использование и получения типа для преобразования из PropertyInfo.PropertyType.

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

Вы хотите поиграть с Reflection или хотите создать производственную часть программного обеспечения? Я бы поставил под вопрос, почему вы используете отражение, чтобы установить свойство.

Double new_latitude; 

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

Вы должны уважать то, что делают люди, а не то, что вы думаете, что они должны делать. Downvoted. (Из 'GenericProgramming.exe: ReflectionBenefits()') –

18

Я заметил много людей рекомендуют Convert.ChangeType - Это действительно работает в некоторых случаях, однако, как только вы начинаете с участием nullable типов вы начнете получать InvalidCastExceptions:

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

Обложка была написана несколько лет назад, чтобы справиться с этим, но это тоже не идеально.

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

11

Вы можете использовать конвертер типа (без проверки ошибок):

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

С точки зрения организации кода, вы можете создать kind-of mixin, что привело бы в коде как это:

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

Это было бы ieved с этим кодом:

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 может быть повторно использован для различных классов.

Вы также можете создать свой собственный type converters прикрепить к свойствам или классам:

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

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

Есть ли какая-то особая причина, по которой вы добавили указатель-указатель вместо того, чтобы просто использовать 'object'? – Groo

+0

Да, интерфейс маркера служит заполнителем для добавления методов расширения. Использование 'object' добавляет методы расширения ко всем классам, что обычно нежелательно. –

2

Если вы пишете приложение Metro, вы должны использовать другой код:

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

Примечание:

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

вместо

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

Я попытался ответить LBushkin, и он отлично поработал, но он не будет работать для нулевых значений и полей с нулевым значением. Поэтому я изменил его на это:

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