Ungültige Umwandlung von 'System.Int32' in '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

Ich erhalte InvalidCastException im obigen Code. Für oben könnte ich einfach schreiben int? nVal = val, aber oben Code wird dynamisch ausgeführt.

Ich erhalte einen Wert (vom nicht nullbaren Typ wie int, float usw.), der in ein Objekt eingewickelt ist (hier val), und ich muss ihn in einem anderen Objekt speichern, indem ich ihn in einen anderen Typ umwandle (der eine nullbare Version sein kann oder nicht) davon). Wann

Ungültige Umwandlung von 'System.Int32' in 'System.Nullable`1 [[System.Int32, mscorlib, Version = 4.0.0.0, Kultur = neutral, PublicKeyToken = b77a5c561934e089]]'.

An int, sollte konvertierbar / typgießbar sein nullable int, worum geht es hier?

Brij
quelle
Ich denke, vielleicht Nullable<T>implementiert Coz nichtIConvertible
V4Vendetta
2
Das ist ziemlich grundlegend. Nullable ist etwas Besonderes. Wenn Sie es in ein Objekt einfügen, wird es entweder null oder ein Boxwert des Wertetyps. Also nach einem Int fragen? in einem Objekt gespeichert macht einfach keinen Sinn. Fragen Sie einfach nach int.
Hans Passant

Antworten:

140

Sie müssen verwenden Nullable.GetUnderlyingType, um den zugrunde liegenden Typ von zu erhalten Nullable.

Dies ist die Methode, mit der ich die Begrenzung von ChangeTypefor überwindeNullable

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

nicht generische Methode:

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);
}
gzaxx
quelle
Um Ihre Methode zu verwenden, müsste ich Folgendes tun: object nVal = ChangeType<int?>(val)Hier muss ich der Methode das generische Argument (T) mitteilen, aber ich habe t(oder typeof (dataType)) zur Verfügung. Wie würde ich Ihre ChangeType-Methode in meinem Szenario aufrufen?
Brij
1
Nicht generische Version hinzugefügt. Sehen Sie, ob es hilft.
Gzaxx
Kompilierungsfehler bei zu bekommen default(conversion), scheint ein ähnliches Problem zu sein.
Brij
Vorsicht @gzaxx as return nullist nicht dasselbe wie default(T). Wenn Sie mit Strukturen zu tun haben, sind das völlig andere Dinge.
Alex
Bei einer nicht generischen Version werden Struktur- und Primitivtypen (wie sie Objekte annehmen und zurückgeben) umrahmt, sodass die Rückgabe von null gültig ist. Jeder, der die Funktionen aufruft, muss dies selbst erledigen.
Gzaxx
9

Für oben könnte ich einfach int schreiben? nVal = val

Das kann man eigentlich auch nicht. Es gibt keine implizite Konvertierung von objectnach Nullable<int>. Es gibt jedoch eine implizite Konvertierung von intnach, Nullable<int>sodass Sie Folgendes schreiben können:

int? unVal = (int)val;

Sie können Nullable.GetUnderlyingTypeMethode verwenden.

Gibt das zugrunde liegende Typargument des angegebenen nullbaren Typs zurück.

Eine generische Typdefinition ist eine Typdeklaration wie Nullable, die eine Typparameterliste enthält, und die Typparameterliste deklariert einen oder mehrere Typparameter. Ein geschlossener generischer Typ ist eine Typdeklaration, bei der ein bestimmter Typ für einen Typparameter angegeben wird.

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

Hier ist ein DEMO.

Soner Gönül
quelle
2

Ich denke, ich sollte erklären, warum die Funktion nicht funktioniert:

1- Die Zeile, die die Ausnahme auslöst, lautet wie folgt:

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

Tatsächlich sieht die Funktionssuche im Array Convert.ConvertTypes danach, ob das Targer eine Aufzählung ist, und wenn nichts gefunden wird, löst es die obige Ausnahme aus.

2- Die Convert.ConvertTypes werden wie folgt initialisiert:

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

Da sich das int?also nicht im ConvertTypes-Array und nicht in einer Aufzählung befindet, wird die Ausnahme ausgelöst.

Um fortzufahren, damit die Funktion Convert.ChnageType funktioniert, haben Sie:

  1. Das zu konvertierende Objekt ist IConvertible

  2. Der Zieltyp befindet sich innerhalb der ConvertTypes und nicht Emptynoch DBNull(Es gibt einen expliziten Test für diese beiden mit Wurfausnahme)

Dieses Verhalten ist darauf zurückzuführen, dass int(und alle anderen Standardtypen) Convert.DefaultToTypeIConvertibale.ToType implementation. and here is the code of theDefaultToType extractedverwendetILSpy

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

Andererseits wird die Besetzung von der Nullable-Klasse selbst implementiert und die Definition lautet:

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