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
と書くことができますが、上記のコードは動的に実行されます。
私はオブジェクト(ここではval)にラップされた値(int、floatなどのnull不可タイプ)を取得していますが、別のタイプ(null可能バージョンにできるまたはできない)にキャストすることで別のオブジェクトに保存する必要がありますそれの)。いつ
'System.Int32'から 'System.Nullable`1 [[System.Int32、mscorlib、Version = 4.0.0.0、Culture = neutral、PublicKeyToken = b77a5c561934e089]]]への無効なキャスト。
int
は、nullable int
に変換可能/型変換可能である必要があります。ここでの問題は何ですか?
Nullable
の基本型を取得するには、Nullable.GetUnderlyingType
を使用する必要があります。
これは、ChangeType
の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);
}
非汎用メソッド:
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);
}
上記の場合、私は単にintを書くことができますか? nVal = val
実際、それもできません。 object
からNullable<int>
への暗黙的な変換はありません。しかし、はint
からNullable<int>
への暗黙的な変換なので、これを書くことができます:
int? unVal = (int)val;
Nullable.GetUnderlyingType
メソッドを使用できます。
指定されたnull許容型の基になる型引数を返します。
ジェネリック型定義は、型パラメーターリストを含むNullableなどの型宣言であり、型パラメーターリストは1つ以上の型パラメーターを宣言します。閉じられたジェネリック型は、特定の型が型パラメーターに指定されている型宣言です。
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
です。
関数が機能しない理由を説明する必要があると思います。
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が機能するために次のようにします。
変換されるオブジェクトはIConvertibleです
ターゲットタイプはConvertTypes内にあり、Empty
やDBNull
ではありません(スロー例外のある2つには明示的なテストがあります)
この動作は、int
(および他のすべてのデフォルト型)がConvert.DefaultToType
をIConvertibale.ToType implementation. and here is the code of the
DefaultToTypeextracted
として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;
}