Установка свойства с помощью отражения со строковым значением

312

Я хотел бы установить свойство объекта через 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» не может быть преобразован в тип «System.Double».

Как я могу преобразовать значение в правильный тип, на основе propertyInfo?

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

Ответы:

527

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

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

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
LBushkin
источник
Просмотрите ответ @AliKaraca ниже. И этот, и приведенный ниже, быстрые и свободные, но делают работу для распространенных типов.
Аарон Худон
Есть TryChangeTypeили CanChangeType?
Шимми Вейцхандлер
34

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

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

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

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

Джон Сондерс
источник
1
ОП, вероятно, хочет получить общий ответ для установки свойства любого типа, которое имеет явное преобразование из строки.
Даниэль Уорвикер
Хорошая точка зрения. Я отредактирую и укажу на настоящих ответчиков, или удалю мой, если кто-то добавит то, что я сказал об остальном пространстве имен.
Джон Сондерс
19

Я заметил, что многие люди рекомендуют 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

таблетка
источник
13

Я попробовал ответ от Л.Бушкина, и он работал отлично, но он не будет работать для пустых значений и полей, допускающих обнуление. Итак, я изменил это на это:

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);
}
Ашкан Сироус
источник
Я должен сказать спасибо, когда встретил это дело, и это единственное решение. спасибо ~!
Франва
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);

С точки зрения организации кода, вы могли бы создать своего рода миксин, который привел бы к такому коду:

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

Это будет достигнуто с помощью этого кода:

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

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

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

[TypeConverter(typeof(LatitudeConverter))]
public class Latitude { ... }
Jordão
источник
Есть ли какая-то особая причина, по которой вы добавили маркерный интерфейс вместо того, чтобы просто использовать object?
Groo
1
Да, интерфейс маркера служит заполнителем для добавления методов расширения. Использование objectдобавит методы расширения ко всем классам, что обычно нежелательно.
Джордао
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 );
tvanfosson
источник
4

Я отвечу на это общим ответом. Обычно эти ответы не работают с гидами. Вот рабочая версия с направляющими тоже.

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
Это должен быть принятый ответ. Это также работает с GUID <3. Спасибо, Али (это прозвище моей дочери)
Кэтэлин Рэдой,
3

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

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

//But this will cause problems if your string value IsNullOrEmplty...
bytebender
источник
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");
Serhiy
источник
0

Использование следующего кода должно решить вашу проблему:

item.SetProperty(prop.Name, Convert.ChangeType(item.GetProperty(prop.Name).ToString().Trim(), prop.PropertyType));
Марко Сотто
источник
-9

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

Double new_latitude;

Double.TryParse (value, out new_latitude);
ship.Latitude = new_latitude;
clemahieu
источник
1
Вы должны уважать то, что люди пытаются делать, а не то, что, по вашему мнению, они должны делать. Downvoted. (From GenericProgramming.exe:ReflectionBenefits())
Петър Петров