Type t = typeof(int?); //will get this dynamically
object val = 5; //will get this dynamically
object nVal = Convert.ChangeType(val, t);//getting exception here
Otrzymuję InvalidCastException w powyższym kodzie. Powyżej mógłbym po prostu napisać int? nVal = val
, ale powyższy kod jest wykonywany dynamicznie.
Otrzymuję wartość (typu nie dopuszczającego wartości null, takiej jak int, float itp.) Zawiniętą w obiekt (tutaj val) i muszę zapisać ją w innym obiekcie, rzutując ją na inny typ (który może lub nie może być wersją zerową tego). Gdy
Nieprawidłowe rzutowanie z „System.Int32” na „System.Nullable” 1 [[System.Int32, mscorlib, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089]] ”.
int
, Powinny być wymienialne / typu rzutować na nullable int
to, co jest tu problem?
Nullable<T>
nie implementujeIConvertible
Odpowiedzi:
Musisz użyć,
Nullable.GetUnderlyingType
aby uzyskać podstawowy typNullable
.To jest metoda, której używam, aby przezwyciężyć ograniczenie
ChangeType
forNullable
public static T ChangeType<T>(object value) { var t = typeof(T); if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { if (value == null) { return default(T); } t = Nullable.GetUnderlyingType(t); } return (T)Convert.ChangeType(value, t); }
metoda nieogólna:
public static object ChangeType(object value, Type conversion) { var t = conversion; if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { if (value == null) { return null; } t = Nullable.GetUnderlyingType(t); } return Convert.ChangeType(value, t); }
źródło
object nVal = ChangeType<int?>(val)
tutaj muszę powiedzieć metodzie o ogólnym argumencie (T), ale mam do dyspozycjit
(lub typeof (dataType)). Jak mogę wywołać metodę ChangeType w moim scenariuszu?default(conversion)
, wygląda na podobny problem.return null
to nie to samo codefault(T)
. Jeśli masz do czynienia ze strukturami, to są to zupełnie inne rzeczy.Właściwie tego też nie możesz zrobić. Nie ma niejawnej konwersji z
object
naNullable<int>
. Ale nie jest niejawna konwersja zint
doNullable<int>
tak można napisać tak:int? unVal = (int)val;
Możesz użyć
Nullable.GetUnderlyingType
metody.Type t = typeof(int?); //will get this dynamically Type u = Nullable.GetUnderlyingType(t); object val = 5; //will get this dynamically object nVal = Convert.ChangeType(val, u);// nVal will be 5
Oto plik
DEMO
.źródło
Myślę, że powinienem wyjaśnić, dlaczego funkcja nie działa:
1- Linia, która zgłasza wyjątek, wygląda następująco:
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo", new object[] { value.GetType().FullName, targetType.FullName }));
w rzeczywistości funkcja wyszukuje w tablicy Convert.ConvertTypes, po czym sprawdza, czy celem jest Enum, a gdy nic nie zostanie znalezione, zgłasza wyjątek powyżej.
2- plik Convert.ConvertTypes jest inicjowany jako:
Convert.ConvertTypes = new RuntimeType[] { (RuntimeType)typeof(Empty), (RuntimeType)typeof(object), (RuntimeType)typeof(DBNull), (RuntimeType)typeof(bool), (RuntimeType)typeof(char), (RuntimeType)typeof(sbyte), (RuntimeType)typeof(byte), (RuntimeType)typeof(short), (RuntimeType)typeof(ushort), (RuntimeType)typeof(int), (RuntimeType)typeof(uint), (RuntimeType)typeof(long), (RuntimeType)typeof(ulong), (RuntimeType)typeof(float), (RuntimeType)typeof(double), (RuntimeType)typeof(decimal), (RuntimeType)typeof(DateTime), (RuntimeType)typeof(object), (RuntimeType)typeof(string) };
Więc ponieważ
int?
nie ma w tablicy ConvertTypes i nie jest Enum, zgłaszany jest wyjątek.Aby wznowić, aby funkcja Convert.ChnageType działała, masz:
Obiekt do konwersji to IConvertible
Typ docelowy znajduje się w ConvertTypes, a nie
Empty
norDBNull
(istnieje test jawny dla tych dwóch z wyjątkiem rzutu)Dzieje się tak, ponieważ
int
(i wszystkie inne typy domyślne) używaConvert.DefaultToType
jako IConvertibale.ToTypeimplementation. and here is the code of the
DefaultToTypeextracted
usingILSpy
internal static object DefaultToType(IConvertible value, Type targetType, IFormatProvider provider) { if (targetType == null) { throw new ArgumentNullException("targetType"); } RuntimeType left = targetType as RuntimeType; if (left != null) { if (value.GetType() == targetType) { return value; } if (left == Convert.ConvertTypes[3]) { return value.ToBoolean(provider); } if (left == Convert.ConvertTypes[4]) { return value.ToChar(provider); } if (left == Convert.ConvertTypes[5]) { return value.ToSByte(provider); } if (left == Convert.ConvertTypes[6]) { return value.ToByte(provider); } if (left == Convert.ConvertTypes[7]) { return value.ToInt16(provider); } if (left == Convert.ConvertTypes[8]) { return value.ToUInt16(provider); } if (left == Convert.ConvertTypes[9]) { return value.ToInt32(provider); } if (left == Convert.ConvertTypes[10]) { return value.ToUInt32(provider); } if (left == Convert.ConvertTypes[11]) { return value.ToInt64(provider); } if (left == Convert.ConvertTypes[12]) { return value.ToUInt64(provider); } if (left == Convert.ConvertTypes[13]) { return value.ToSingle(provider); } if (left == Convert.ConvertTypes[14]) { return value.ToDouble(provider); } if (left == Convert.ConvertTypes[15]) { return value.ToDecimal(provider); } if (left == Convert.ConvertTypes[16]) { return value.ToDateTime(provider); } if (left == Convert.ConvertTypes[18]) { return value.ToString(provider); } if (left == Convert.ConvertTypes[1]) { return value; } if (left == Convert.EnumType) { return (Enum)value; } if (left == Convert.ConvertTypes[2]) { throw new InvalidCastException(Environment.GetResourceString("InvalidCast_DBNull")); } if (left == Convert.ConvertTypes[0]) { throw new InvalidCastException(Environment.GetResourceString("InvalidCast_Empty")); } } throw new InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo", new object[] { value.GetType().FullName, targetType.FullName })); }
z drugiej strony rzutowanie jest implementowane przez samą klasę Nullable, a definicja to:
public static implicit operator T?(T value) { return new T?(value); } public static explicit operator T(T? value) { return value.Value; }
źródło