Ustawienie właściwości przez odbicie za pomocą wartości ciągu

312

Chciałbym ustawić właściwość obiektu poprzez odbicie, z wartością typu string. Załóżmy na przykład, że mam Shipklasę, której właściwość Latitudeto double.

Oto, co chciałbym zrobić:

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

Jak to powoduje ArgumentException:

Obiekt typu „System.String” nie może zostać przekonwertowany na typ „System.Double”.

Jak przekonwertować wartość na odpowiedni typ, na podstawie propertyInfo?

David Hodgson
źródło
1
Pytanie do Ciebie: czy jest to część niestandardowego rozwiązania ORM?
user3308043

Odpowiedzi:

527

Możesz użyć Convert.ChangeType()- pozwala na użycie informacji o środowisku wykonawczym na dowolnym IConvertibletypie w celu zmiany formatów reprezentacji. Jednak nie wszystkie konwersje są możliwe i może być konieczne napisanie specjalnej logiki wielkości liter, jeśli chcesz obsługiwać konwersje z typów, które nie są IConvertible.

Odpowiedni kod (bez obsługi wyjątków lub logiki specjalnych przypadków) to:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
LBushkin
źródło
Przejrzyj odpowiedź @AliKaraca poniżej. Zarówno to, jak i poniższe są szybkie i luźne, ale wykonują pracę dla typowych typów.
Aaron Hudon
Czy istnieje TryChangeTypeor CanChangeType?
Shimmy Weitzhandler,
34

Jak powiedziało kilka innych, chcesz użyć Convert.ChangeType:

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

W rzeczywistości polecam spojrzeć na całą Convertklasę .

Ta klasa i wiele innych przydatnych klas są częścią SystemPrzestrzeni nazw . Uważam, że warto skanować tę przestrzeń nazw co roku, aby sprawdzić, które funkcje mi umknęły. Spróbuj!

John Saunders
źródło
1
OP prawdopodobnie chce ogólnej odpowiedzi, aby ustawić właściwość dowolnego typu, która ma oczywistą konwersję z łańcucha.
Daniel Earwicker
Słuszna uwaga. Zedytuję i wskażę prawdziwych użytkowników odpowiadających lub usunę moje, jeśli ktoś doda to, co powiedziałem o reszcie przestrzeni nazw.
John Saunders,
13

Próbowałem odpowiedzi od LBushkina i zadziałało świetnie, ale nie zadziała dla wartości zerowych i pól zerowalnych. Zmieniłem to na:

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);
}
Ashkan Sirous
źródło
Muszę podziękować, gdy spotkałem ten przypadek i jest to jedyne rozwiązanie. dzięki ~!
Franva
11

Możesz użyć konwertera typów (bez sprawdzania błędów):

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

Jeśli chodzi o organizację kodu, możesz stworzyć rodzaj miksu, który dałby taki kod:

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

Można to osiągnąć za pomocą tego kodu:

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 mogą być ponownie wykorzystane dla wielu różnych klas.

Możesz także utworzyć własne konwertery typu, aby dołączyć je do swoich właściwości lub klas:

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

[TypeConverter(typeof(LatitudeConverter))]
public class Latitude { ... }
Jordão
źródło
Czy istnieje jakiś szczególny powód, dla którego dodałeś insterface znacznika zamiast po prostu używać object?
Groo,
1
Tak, interfejs znacznika służy jako symbol zastępczy, do którego można dodać metody rozszerzenia. Użycie objectspowoduje dodanie metod rozszerzenia do wszystkich klas, co nie jest ogólnie pożądane.
Jordão
6

Prawdopodobnie szukasz Convert.ChangeTypemetody. Na przykład:

Ship ship = new Ship();
string value = "5.5";
PropertyInfo propertyInfo = ship.GetType().GetProperty("Latitude");
propertyInfo.SetValue(ship, Convert.ChangeType(value, propertyInfo.PropertyType), null);
John Calsbeek
źródło
5

Używanie Convert.ChangeTypei pobieranie typu do konwersji z PropertyInfo.PropertyType.

propertyInfo.SetValue( ship,
                       Convert.ChangeType( value, propertyInfo.PropertyType ),
                       null );
tvanfosson
źródło
4

Odpowiem na to ogólną odpowiedzią. Zwykle te odpowiedzi nie działają z przewodnikami. Oto działająca wersja z prowadnicami.

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); 
Ali Karaca
źródło
1
To powinna być zaakceptowana odpowiedź. Działa również z identyfikatorami GUID <3. Dzięki, Ali (to przezwisko mojej córki)
Cătălin Rădoi
3

Lub możesz spróbować:

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

//But this will cause problems if your string value IsNullOrEmplty...
bytebender
źródło
2

Jeśli piszesz aplikację Metro, powinieneś użyć innego kodu:

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

Uwaga:

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

zamiast

ship.GetType().GetProperty("Latitude");
Serhiy
źródło
0

Użycie następującego kodu powinno rozwiązać problem:

item.SetProperty(prop.Name, Convert.ChangeType(item.GetProperty(prop.Name).ToString().Trim(), prop.PropertyType));
Marco Sotto
źródło
-9

Czy chcesz się bawić z Reflection, czy chcesz zbudować produkcyjne oprogramowanie? Chciałbym zapytać, dlaczego używasz refleksji, aby ustawić właściwość.

Double new_latitude;

Double.TryParse (value, out new_latitude);
ship.Latitude = new_latitude;
clemahieu
źródło
1
Powinieneś szanować to, co ludzie próbują robić, a nie to, co Twoim zdaniem muszą robić. Doceniony. (Od GenericProgramming.exe:ReflectionBenefits())
Петър Петров