2013-08-02 23 views
58
Type t = typeof(int?); //will get this dynamically 
object val = 5; //will get this dynamically 
object nVal = Convert.ChangeType(val, t);//getting exception here 

I InvalidCastException in obigem Code bin immer. Für oben könnte ich einfach int? nVal = val schreiben, aber oben Code wird dynamisch ausgeführt.Ungültige Umwandlung von 'System.Int32' auf ‚System.Nullable`1 [[System.Int32 Mscorlib]]

Ich bekomme einen Wert (von nicht Nullable Typ wie Int, Float, etc) in ein Objekt (hier val) gewickelt, und ich muss es auf ein anderes Objekt speichern, indem Sie es in einen anderen Typ (das kann oder kann keine NULL-fähige Version davon sein). Wenn

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

Ein int sollte konvertierbar sein/Typ-gießbaren zu nullable int, was ist das Problem hier?

+0

Ich denke, vielleicht coz 'Nullable ' implementiert nicht 'IConvertible' – V4Vendetta

+2

Dies ist ziemlich grundlegend. Nullable ist speziell, wenn Sie es in ein Objekt einfügen, wird es entweder null oder wird zu einem Boxed-Wert des Werttyps. Also nach einem Int fragen? in einem Objekt gespeichert ist einfach nicht sinnvoll. Fragen Sie einfach nach int. –

Antwort

87

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

Dies ist die Methode, die ich 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); 
} 

nicht gattungsgemäßes Verfahren zur Begrenzung von ChangeType zu überwinden verwenden:

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

um Ihre Methode zu verwenden, müsste ich etwas tun wie: 'Objekt nVal = ChangeType (val)', hier muss ich die Methode über das generische Argument (T) sagen, aber ich habe 't' (oder typeof (dataType)) zu meiner 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

+0

Kompilierungsfehler bei 'default (Konvertierung)', scheint wie ein ähnliches Problem. – Brij

3

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

Eigentlich kann man das auch nicht tun. Es gibt keine implizite Konvertierung von object zu Nullable<int>. Aber es ist eine implizite Konvertierung von int zu Nullable<int> so können Sie schreiben:

int? unVal = (int)val; 

Sie können Nullable.GetUnderlyingType Methode verwenden.

Gibt das zugrunde liegende Argument des angegebenen Nullwerttyps zurück.

A generic Typdefinition eine Typdeklaration, wie Nullable, , die einen Typ Parameterliste enthält, und die Art Parameterliste erklärt, eine oder mehrere Typ-Parameter. Ein geschlossener generischer Typ ist eine Deklaration des Typs , in 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 ein DEMO.

1

Ich glaube, 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 
    })); 

in der Tat die Funktion Suche im Array Convert.ConvertTypes sieht danach, ob der targer ein Enum ist und wenn nichts gefunden wird, wirft er die obige Ausnahme.

2- die Convert.ConvertTypes initialisiert als:

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

So seit dem int? ist nicht in der ConvertTypes Array und kein Enum die Ausnahme ausgelöst wird.

So fortzusetzen, für die Funktion Convert.ChnageType Sie müssen arbeiten:

  1. Das Objekt umgewandelt werden IConvertible ist

  2. Der Zieltyp innerhalb der ConvertTypes und nicht Empty noch DBNull (Es gibt einen explict Test auf den beiden mit throw Ausnahme)

Dieses Verhalten ist, weil int (und alle anderen Standardtypen) verwendet Convert.DefaultToType als IConvertibale.ToType implementation. and here is the code of the DefaultToType extractedILSpy

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

in der anderen Hand die Besetzung von Nullable-Klasse implementiert wird selbst 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; 
} 
Verwandte Themen