Rozważmy następujący przykładowy kod:
class SampleClass
{
public long SomeProperty { get; set; }
}
public void SetValue(SampleClass instance, decimal value)
{
// value is of type decimal, but is in reality a natural number => cast
instance.SomeProperty = (long)value;
}
Teraz muszę zrobić coś podobnego poprzez refleksję:
void SetValue(PropertyInfo info, object instance, object value)
{
// throws System.ArgumentException: Decimal can not be converted to Int64
info.SetValue(instance, value)
}
Zauważ, że nie mogę założyć, że PropertyInfo zawsze reprezentuje długość, ani ta wartość nie zawsze jest dziesiętna. Wiem jednak, że wartość można rzutować na odpowiedni typ dla tej właściwości.
Jak mogę przekonwertować parametr „value” na typ reprezentowany przez instancję PropertyInfo poprzez odbicie?
c#
reflection
casting
jeroenh
źródło
źródło
Convert.ChangeType(value, property.PropertyType);
nadal może się nie powieść, jeślivalue
nie zaimplementujeIConvertible
interfejsu. Na przykład, jeśliinfo.PropertyType
jest jakiśIEnumerable
Odpowiedź Thomasa działa tylko dla typów, które implementują interfejs IConvertible:
Ten kod kompiluje wyrażenie linq, które wykonuje rozpakowywanie (w razie potrzeby) i konwersję:
public static object Cast(this Type Type, object data) { var DataParam = Expression.Parameter(typeof(object), "data"); var Body = Expression.Block(Expression.Convert(Expression.Convert(DataParam, data.GetType()), Type)); var Run = Expression.Lambda(Body, DataParam).Compile(); var ret = Run.DynamicInvoke(data); return ret; }
Wynikowe wyrażenie lambda jest równe (TOut) (TIn) Data, gdzie TIn to typ oryginalnych danych, a TOut to podany typ
źródło
IEnumerable<object>
(tam, gdzie te obiekty są łańcuchami)IEnumerable<string>
. NiestetyUnable to cast object of type 'System.Collections.Generic.IEnumerable'1[System.Object]' to type 'System.Collections.Generic.IEnumerable'1[System.String]'.
Odpowiedź Thomasa jest poprawna, ale pomyślałem, że dodam swoje odkrycie, że Convert.ChangeType nie obsługuje konwersji na typy dopuszczające wartość null. Aby obsłużyć typy dopuszczające wartość null, użyłem następującego kodu:
void SetValue(PropertyInfo info, object instance, object value) { var targetType = info.PropertyType.IsNullableType() ? Nullable.GetUnderlyingType(info.PropertyType) : info.PropertyType; var convertedValue = Convert.ChangeType(value, targetType); info.SetValue(instance, convertedValue, null); }
Ten kod wykorzystuje następującą metodę rozszerzenia:
public static class TypeExtensions { public static bool IsNullableType(this Type type) { return type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)); }
źródło
Przyczyniając się do odpowiedzi Jeroenh, dodałbym, że Convert.ChangeType ulega awarii z wartością null, więc wiersz do uzyskania przekonwertowanej wartości powinien wyglądać następująco:
var convertedValue = value == null ? null : Convert.ChangeType(value, targetType);
źródło
Gdy typ jest wartością Nullable Guid, żadne z proponowanych powyżej rozwiązań nie działa. W miejscu zgłaszany jest nieprawidłowy rzut z wyjątku „
System.DBNull
„ do ”System.Guid
Convert.ChangeType
Aby naprawić tę zmianę w:
var convertedValue = value == System.DBNull.Value ? null : Convert.ChangeType(value, targetType);
źródło
DBNull.Value
zamiast po prostu pobieraćnull
wartości null z bazy danych przez ADO.Net. To samo zobaczysz na przykład z wartością nullable int.To jest bardzo stare pytanie, ale pomyślałem, że wbijam się w pracowników Google w ASP.NET Core.
W ASP.NET Core
.IsNullableType()
jest chroniony (między innymi), więc kod jest nieco inny. Oto odpowiedź @ jeroenh's zmodyfikowana do pracy w ASP.NET Core:void SetValue(PropertyInfo info, object instance, object value) { Type proptype = info.PropertyType; if (proptype.IsGenericType && proptype.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { proptype = new NullableConverter(info.PropertyType).UnderlyingType; } var convertedValue = Convert.ChangeType(value, proptype); info.SetValue(instance, convertedValue); }
źródło