Встановлення властивості шляхом відображення зі значенням рядка


312

Я хотів би встановити властивість об'єкта через Reflection зі значенням типу string. Наприклад, припустимо, що у мене є Shipклас, властивість Latitudeякого є a 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);

Перегляньте відповідь @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 { ... }

Чи є якась конкретна причина, чому ви додали маркерну поверхню замість того, щоб просто використовувати object?
Гроо

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

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. Дякую, Алі (це прізвисько моєї дочки)
Cătălin Rădoi

3

Або ви можете спробувати:

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

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

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

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;

1
Ви повинні поважати те, що люди намагаються зробити, а не те, що ви думаєте, що вони повинні робити. Захищений. (Від GenericProgramming.exe:ReflectionBenefits())
Петър Петров
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.