2009-07-06 21 views
251

Chciałbym ustawić właściwość obiektu poprzez odbicie, o wartości typu string. Załóżmy na przykład, że mam klasę Ship, z właściwością Latitude, która jest double.Ustawianie właściwości za pomocą odbicia z wartością ciągu znaków

Oto, co chciałabym zrobić:

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

jak jest, to rzuca ArgumentException:

obiektu typu 'System.String' nie może być przekształcony do typu „System. Podwójnie'.

Jak przekonwertować wartość na właściwy typ, na podstawie propertyInfo?

+1

Pytanie dla Ciebie: czy jest to część niestandardowego rozwiązania ORM? – user3308043

Odpowiedz

419

Można użyć Convert.ChangeType() - Pozwala to na użycie informacji o czasie pracy dla dowolnego typu IConvertible, aby zmienić formaty reprezentacji. Nie wszystkie konwersje są jednak możliwe i może zajść potrzeba napisania specjalnej logiki, jeśli chcesz obsługiwać konwersje z typów, które nie są IConvertible.

odpowiedni kod (bez obsługi wyjątków lub szczególnym przypadku logiki) byłoby:

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

Ponieważ kilka mówili inni, którego chcesz użyć Convert.ChangeType:

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

W rzeczywistości, polecam spojrzeć na całą Convert Class.

Ta klasa i wiele innych użytecznych klas należy do zakresu System Namespace. Uważam, że warto skanować tę przestrzeń nazw każdego roku, aby zobaczyć, jakie funkcje przeoczyłem. Spróbuj!

+1

OP prawdopodobnie chce ogólnej odpowiedzi, dla ustawienia właściwości dowolnego typu, który ma oczywistą konwersję z ciągu znaków. –

+0

Dobra uwaga. Będę edytować i wskazywać na prawdziwych odbierających, lub usunie moje, jeśli ktoś doda to, co powiedziałem o reszcie przestrzeni nazw. –

3

Albo można spróbować:

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

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

Pewnie szuka sposobu Convert.ChangeType. 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); 
5

Korzystanie Convert.ChangeType i coraz typ konwersji z PropertyInfo.PropertyType.

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

Szukasz rozrywki w Reflectionie lub szukasz oprogramowania do produkcji? 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; 
+0

Powinieneś szanować to, co ludzie próbują zrobić, a nie to, co Twoim zdaniem muszą zrobić. Wycofane. (Z 'GenericProgramming.exe: ReflectionBenefits()') –

18

Zauważyłem wiele osób zalecają Convert.ChangeType - To działa na niektórych przypadkach jednak jak najszybciej rozpocząć z udziałem nullable rodzajów zaczniesz otrzymania InvalidCastExceptions:

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

Opakowanie zostało napisane kilka lat temu, aby poradzić sobie z tym, ale to też nie jest doskonałe.

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

11

Można użyć konwertera typu (nie sprawdzanie 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); 

chodzi o organizację kodu, można utworzyć kind-of mixin które mogłoby spowodować kodu podobnego to:

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

To byłby ieved z tym kodem:

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 do różnych klas.

Można również tworzyć własne type converters dołączyć do właściwości lub klasach:

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

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

Czy istnieje jakiś szczególny powód, dla którego dodałeś markera insterface zamiast po prostu używając 'object'? – Groo

+0

Tak, interfejs znacznika służy jako symbol zastępczy, do którego można dodać metody rozszerzeń. Użycie 'obiektu' spowodowałoby dodanie metod rozszerzeń do wszystkich klas, co nie jest ogólnie pożądane. –

2

Jeśli piszesz aplikację Metro, należy 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 z

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

Próbowałem odpowiedź od LBushkin i działało świetnie, ale nie będzie działać dla wartości pustych i pól zerowalnych. Więc 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); 
} 
Powiązane problemy