2

プロシージャによって返されるデータベース値を処理するための変換メソッドを作成しました。次のようになります。

public static T GetVerifiedValue<T>(this IDataRecord record, int index)
{
    object value = record[index];

    if (value is string)
        value = string.IsNullOrEmpty(value as string) ? null : value;

    Type nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T));

    if (nullableUnderlyingType != null)
    {
        if (nullableUnderlyingType.IsEnum)
            return value == null || value.Equals(DBNull.Value) ? default(T) : (T)Enum.ToObject(nullableUnderlyingType, value);
    }

    /*
    //This is my try on solving the problem, but won't compile 
    //because T has no struct constraint
    if (value is ValueType)
    {
        ValueType structValue = (ValueType)value;
        return value.Equals(DBNull.Value) ? default(T) : (T)structValue;
    }
    */

    return value == null || value.Equals(DBNull.Value) ? default(T) : (T)value;
}

問題は、データベースが整数を返す場合、value変数に が含まれ、が のint場合、 が得られることです。TshortInvalidCastException

この状況を処理するためにこのメソッドを改善するにはどうすればよいですか? メソッドのユーザーがこの種の問題を気にせず、ダブルキャストする必要がないことを望みます。これは可能ですか?

4

3 に答える 3

4

ボックス化された値の型を正しい値の型にキャストしてから、別の値の型に再度キャストすることしかできません (そのようなキャストがサポートされている場合)。

ただし、Convertクラスはあります。intに箱入りのがあった場合は、valueに渡して返品Convert.ToInt16(value)を受け取ることができます。shortおそらくConvert(キャストの代わりに) クラスを使用するタイプの数は少ないswitchため、Convert.

static bool MaybeConvert<TOutput>(object value, out TOutput result) {
    output = default(TOutput);
    switch(typeof(TOutput)) {
        case typeof(short):
            result = Convert.ToInt16(value);
            return true;

        ...

        default:
            return false;
    }
}

Convert32 ビット整数フィールドで作業している場合でも、何らかの計算または集計が行われると、それらが 64 ビット整数として返されることがあるため、私はデータベースの結果で多くのことを使用します。タイプをチェックして自分で非常に具体的なキャストを行うよりも、使用Convert.ToInt32する方がはるかに簡単です。

于 2011-10-04T13:58:16.330 に答える
3

結果をasとして使用してdynamicからキャストすると、ボクシングが存在する場合に機能するはずです。

public static T GetValue<T>(this IDataRecord record, int index)
{
    dynamic result = record[index];
    return (result == null || result == DBNull.Value) ? default(T) : (T)result;
}

ちなみに、これを簡単に扱っているEricLippertのブログ投稿があります。

于 2011-10-04T14:01:11.197 に答える
2

私は使用する方法を見つけましたConvert.ChangeType(object, type)(説明についてはコードのコメントを参照してください):

public static T GetVerifiedValue<T>(this IDataRecord record, int index)
{
    object value = record[index];

    if (value == null || value.Equals(DBNull.Value))
        return default(T);

    //This handles nullable values, because sometimes there is a need for
    //a casting on the underlying value before the final cast
    Type nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T));

    if (nullableUnderlyingType != null)
    {
        if (nullableUnderlyingType.IsEnum)
            return (T)Enum.ToObject(nullableUnderlyingType, value);
        else
            return (T)Convert.ChangeType(value, nullableUnderlyingType);
    }

    //Enums must be handled before the ValueTypes, becouse
    //enums are also ValueTypes and using Convert.ChangeType with it will fail
    if (typeof(T).IsEnum)
        return (T)Enum.ToObject(typeof(T), value);


    //######################################################################
    //Here is the trick: as Convert.ChangeType returns an object,
    //it is compatible with the unconstrained T. Worked nicely.
    if (value is ValueType)
    {
        ValueType structValue = (ValueType)value;
        return (T)Convert.ChangeType(structValue, typeof(T));
    }
    //######################################################################


    if (value is string)
        value = string.IsNullOrEmpty(value as string) ? null : value;

    return (T)value;
}
于 2011-10-04T15:11:03.323 に答える