Недійсний приклад із "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.