Недопустимый литой из системы.Типа int32' к 'системе.И Nullable`1[[Система.Типа 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, но выше код выполняется динамически.

Я получаю значение (ненулевого типа, например int, float и т. д.), завернутое в объект (здесь val), и я должен сохранить его в другой объект, приведя его к другому типу(который может или не может быть его нулевой версией). Когда

недопустимый литой из 'системы.Типа int32' к 'системе.И Nullable`1[[Система.Типа int32, mscorlib, Version=4.0.0.0, Culture=нейтральный, PublicKeyToken=b77a5c561934e089]]'.

An int, должно быть конвертируемым / тип-castable к nullable int, в чем здесь проблема ?

3 66

3 ответа:

вы должны использовать 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);
}

для Выше я мог бы просто написать int? nVal = val

на самом деле, вы тоже не можете этого сделать. Нет никакого неявного преобразования из object до Nullable<int>. Но там и неявное преобразование из int до Nullable<int> Так что вы можете писать так:

int? unVal = (int)val;

можно использовать Nullable.GetUnderlyingType метод.

возвращает базовый аргумент типа указанного типа nullable.

определение универсального типа-это объявление типа, например 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

здесь a DEMO.

я думаю, что я должен объяснить, почему функция не работает:

1-строка, которая вызывает исключение, выглядит следующим образом:

throw new InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo", new object[]
  {
    value.GetType().FullName, 
    targetType.FullName
    }));

в самом деле, функция поиска в массиве преобразования.ConvertTypes после этого он видит, является ли targer перечислением, и когда ничего не найдено, он бросает исключение выше.

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 и не является перечислением исключение.

Итак, чтобы возобновить, для функции преобразования.ChnageType для работы у вас есть:

  1. объект для преобразования является IConvertible

  2. целевой тип находится в пределах ConvertTypes, а не Empty, ни DBNull (есть явный тест на этих двух с исключением броска)

такое поведение происходит потому, что int (и все другие типы по умолчанию) использует Convert.DefaultToType как IConvertibale.Тотип implementation. and here is the code of the DefaultToTypeextracted используя 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;
}