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 InvalidCastException dans le code ci-dessus. Pour ce qui précède, je pourrais simplement écrire int? nVal = val
, mais le code ci-dessus s’exécute de manière dynamique.
J'obtiens une valeur (de type non nullable comme int, float, etc.) encapsulée dans un objet (ici val), et je dois l'enregistrer dans un autre objet en le convertissant en un autre type (qui peut ou non être une version nullable de celui-ci). Quand
Conversion non valide de 'System.Int32' à 'System.Nullable`1 [[System.Int32, mscorlib, version = 4.0.0.0, Culture = neutre, PublicKeyToken = b77a5c561934e089]]'.
Un int
devrait être convertible/transtypé en nullable int
, Quel est le problème ici ?
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);
}
Pour 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
en Nullable<int>
. Mais il y a est une conversion implicite de int
en Nullable<int>
afin que vous puissiez écrire ceci:
int? unVal = (int)val;
Vous pouvez utiliser Nullable.GetUnderlyingType
méthode.
Renvoie l'argument du type sous-jacent du type nullable spécifié.
Une définition de type générique est une déclaration de type, telle que Nullable, 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 une déclaration de type dans laquelle 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
.
Je pense que je devrais expliquer pourquoi la fonction ne fonctionne pas:
1- La ligne qui lève l'exception est la suivante:
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_FromTo", new object[]
{
value.GetType().FullName,
targetType.FullName
}));
en fait, la fonction recherche dans le tableau Convert.ConvertTypes après quoi elle voit si la cible est un Enum et quand rien n’est trouvé, elle lève l’exception ci-dessus.
2- Le Convert.ConvertTypes est initialisé en tant que:
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)
};
Donc, depuis le int?
n'est pas dans le tableau ConvertTypes et n'est pas une exception Enum est levée.
Pour résumer, pour que la fonction Convert.ChnageType fonctionne, vous avez:
L'objet à convertir est IConvertible
Le type de cible est dans les ConvertTypes et non pas Empty
ni DBNull
(il existe un test explicite sur ces deux-là avec une exception throw)
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
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 casting 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;
}