103 votes

Invalid cast from 'System.Int32' to '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

Je reçois une InvalidCastException dans le code ci-dessus. Pour le code ci-dessus, je pourrais simplement écrire int? nVal = val mais le code ci-dessus est exécuté dynamiquement.

Je reçois une valeur (de type non nullable comme int, float, etc) enveloppée dans un objet (ici val), et je dois l'enregistrer dans un autre objet en le coulant dans un autre type (qui peut ou non être une version nullable de celui-ci). Lorsque

Invalid cast from 'System.Int32' to 'System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.

Un site int doit être convertible/transférable en nullable int Quel est le problème ici ?

202voto

gzaxx Points 11117

Vous devez utiliser Nullable.GetUnderlyingType pour obtenir le type sous-jacent de Nullable .

C'est la méthode que j'utilise pour surmonter la limitation de ChangeType pour 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);
}

méthode non générique :

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

15voto

Soner Gönül Points 35739

Pour l'exemple ci-dessus, je pourrais simplement écrire int ? nVal = val

En fait, vous ne pouvez pas faire ça non plus. Il n'y a pas de conversion implicite de object a Nullable<int> . Mais il y a est une conversion implicite de int a Nullable<int> pour que tu puisses écrire ça :

int? unVal = (int)val;

Vous pouvez utiliser Nullable.GetUnderlyingType méthode.

Retourne le argument de type sous-jacent du type nullable spécifié.

Une définition de type générique est une déclaration de type, telle que qui contient une liste de paramètres de type, et la liste de paramètres de type déclare un ou plusieurs paramètres de type. Un type générique fermé est un type déclaration de type où un type particulier est spécifié pour un paramètre de type.

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

Voici un DEMO .

3voto

Swift Points 1300

Je pense que je devrais expliquer pourquoi la fonction ne fonctionne pas :

1- La ligne qui lance l'exception est la suivante :

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

en fait la fonction cherche dans le tableau Convert.ConvertTypes après cela elle regarde si le targer est un Enum et si rien n'est trouvé elle lance l'exception ci-dessus.

2- le Convert.ConvertTypes est initialisé comme :

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

Ainsi, depuis que le int? n'est pas dans le tableau ConvertTypes et n'est pas une Enum, une exception est levée.

Donc pour résumer, pour que la fonction Convert.ChnageType fonctionne, vous devez :

  1. L'objet à convertir est IConvertible.

  2. Le type cible se trouve dans le ConvertTypes et non pas dans le Empty ni DBNull (Il y a un test explicite sur ces deux-là avec une exception de jet).

Ce comportement est dû au fait que int (et tous les autres types par défaut) utilise Convert.DefaultToType comme IConvertibale.ToType implementation. and here is the code of the DefaultToType extracted en utilisant 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
    }));
}

d'autre part, le cast est implémenté par la classe Nullable elle-même et la définition est :

public static implicit operator T?(T value)
{
    return new T?(value);
}
public static explicit operator T(T? value)
{
    return value.Value;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X