Недопустимый листинг из 'System.Int32' в 'System.Nullable`1 [[System.Int32, mscorlib]]
Type t = typeof(int?); //will get this dynamically
object val = 5; //will get this dynamically
object nVal = Convert.ChangeType(val, t);//getting exception here
Я получаю InvalidCastException в приведенном выше коде. Для выше я мог просто написать int? nVal = val
, но над кодом выполняется динамически.
Я получаю значение (типа non-nullable типа int, float и т.д.), завернутое в объект (здесь val), и мне нужно сохранить его на другой объект, переведя его на другой тип (который может или не может может быть его нулевой версией). Когда
Недопустимый листинг с 'System.Int32' на 'System.Nullable`1 [[System.Int32, mscorlib, Версия = 4.0.0.0, Культура = нейтральная, PublicKeyToken = b77a5c561934e089]].
An int
, должен быть кабриолет/тип-castable до nullable int
, в чем проблема?
Ответы
Ответ 1
Вы должны использовать Nullable.GetUnderlyingType
, чтобы получить базовый тип Nullable
.
Это метод, который я использую для преодоления ограничения ChangeType
для Nullable
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);
}
не общий метод:
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);
}
Ответ 2
Для выше я мог просто написать int? nVal = val
Собственно, вы тоже не можете этого сделать. Нет никакого неявного преобразования от object
до Nullable<int>
. Но есть неявное преобразование от int
до Nullable<int>
, чтобы вы могли написать это:
int? unVal = (int)val;
Вы можете использовать Nullable.GetUnderlyingType
метод.
Возвращает аргумент базового типа указанного типа с нулевым значением.
Общее определение типа - это объявление типа, такое как Nullable, который содержит список параметров типа и список параметров типа объявляет один или несколько параметров типа. Закрытый общий тип - это тип где конкретный тип указан для параметра типа.
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
Здесь DEMO
.
Ответ 3
Думаю, я должен объяснить, почему функция не работает:
1- Строка, которая генерирует исключение, выглядит следующим образом:
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo", new object[]
{
value.GetType().FullName,
targetType.FullName
}));
на самом деле поиск функции в массиве Convert.ConvertTypes после этого он видит, является ли целевой объект Enum, и когда ничего не найдено, он генерирует исключение выше.
2- Преобразование .ConvertTypes инициализируется как:
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)
};
Так как int?
не находится в массиве ConvertTypes, а не в Enum, генерируется исключение.
Итак, чтобы возобновить работу функции Convert.ChnageType, у вас есть:
-
Объект, подлежащий преобразованию, является IConvertible
-
Тип цели находится внутри ConvertTypes, а не Empty
и DBNull
(Существует тег экспликации на этих двух с исключением исключения)
Это связано с тем, что int
(и все другие типы по умолчанию) использует Convert.DefaultToType
как IConvertibale.ToType implementation. and here is the code of the
DefaultToType extracted
с помощью ILSpy
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
}));
}
в других случаях приведение выполняется с помощью класса Nullable, и определение:
public static implicit operator T?(T value)
{
return new T?(value);
}
public static explicit operator T(T? value)
{
return value.Value;
}