Недійсний приклад із "System.Int32" на "System.Nullable`1 [[System.Int32, mscorlib]]


81
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), і я повинен зберегти його в інший об'єкт, перекинувши його в інший тип (який може або не може бути анульованою версією цього). Коли

Недійсний приклад із "System.Int32" у "System.Nullable`1 [[System.Int32, mscorlib, Версія = 4.0.0.0, Культура = нейтральна, PublicKeyToken = b77a5c561934e089]]".

int, Повинен бути конвертованим / типу Перетворювані nullable int, що проблема тут?


Я думаю, можливо, coz Nullable<T>не реалізуєIConvertible
V4Vendetta

2
Це досить принципово. Nullable є особливим, коли ви поміщаєте його в об'єкт, він або стає нульовим, або стає позначеним в поле значенням значення. Отже, запитуєте int? зберігається в об'єкті просто не має сенсу. Просто попросіть int.
Ганс Пассант,

Відповіді:


143

Ви повинні використовувати, Nullable.GetUnderlyingTypeщоб отримати основний тип Nullable.

Це метод, який я використовую, щоб подолати обмеження ChangeTypeforNullable

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);
}

для користування вашим методом мені потрібно було б зробити щось на зразок:, object nVal = ChangeType<int?>(val)тут мені потрібно розповісти методу про загальний аргумент (T), але я маю t(або typeof (dataType)) у своєму розпорядженні. Як я можу викликати ваш метод ChangeType у моєму сценарії?
Brij

1
Додана не загальна версія. Подивіться, чи допомагає це.
gzaxx

Отримання помилки компіляції в default(conversion), схоже на подібну проблему.
Brij

Обережно @gzaxx як return nullне те саме, що default(T). Якщо ви маєте справу зі структурами, це абсолютно різні речі.
Олексій

Неуніверсальна версія включає структурні та примітивні типи (оскільки приймає та повертає об'єкт), тому повернення null є дійсним. Будь-хто, хто викликає функції, повинен буде вирішити це самостійно.
gzaxx

9

Для вищезгаданого я міг би просто написати int? nVal = вал

Власне, ви теж цього не можете зробити. Немає неявного перетворення з 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.


2

Думаю, мені слід пояснити, чому функція не працює:

1- Рядок, що видає виняток, такий:

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

насправді пошук функції в масиві Convert.ConvertTypes після цього перевіряє, чи є цільовим елементом Enum, і коли нічого не знайдено, викине виняток вище.

2- Convert.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 вам потрібно:

  1. Об'єкт, який потрібно перетворити, є IConvertible

  2. Цільовий тип знаходиться в межах ConvertTypes, а не Emptyні DBNull(Існує експлікс-тест для цих двох із виключенням throw)

Ця поведінка пов’язана з тим, що int(і всі інші типи за замовчуванням) використовує Convert.DefaultToTypeяк IConvertibale.ToType implementation. and here is the code of theDefaultToType 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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.