6

私は次の方法を持っています:

public static T ExecuteScalar<T>(
    string query, 
    SqlConnection connection, 
    params SqlParameter[] parameters) where T : new()
{
    // Create SqlCommand
    SqlCommand command = CreateCommand(query, connection, parameters);

    // Execute command using ExecuteScalar
    object result = command.ExecuteScalar();

    // Return value as expected type
    if (result == null || result is DBNull) return default(T);
    return (T)result;
}

MIN_ACTIVE_ROWVERSIONデータベースの を として持ちたいですulong。奇妙なことに..以下の最初のメソッド呼び出しはエラーを生成しますが、2 番目のメソッド呼び出しは正常に動作します。

メソッド呼び出し 1はエラーを生成します。

ulong minActiveRowversion = 
    SqlUtils.ExecuteScalar<ulong>(
        "SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
        , _connectionString);

エラー:

System.InvalidCastException: Specified cast is not valid.

メソッド呼び出し 2は正常に動作します。

ulong minActiveRowversion = 
    (ulong)SqlUtils.ExecuteScalar<long>(
        "SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
        , _connectionString);

command.ExecuteScalar() メソッドの結果が次のようになるため、それがどのように可能かわかりません。

object result       | 1955612
result.GetType()    | {Name = "Int64" FullName = "System.Int64"}
  1. 最初のシナリオが不可能で、2 番目のシナリオが機能する理由を教えてもらえますか?
  2. シナリオ1を使用できるように、それを解決する方法を誰かに教えてもらえますか.
4

2 に答える 2

5

どうして

値の型を元の型にのみボックス化解除できます。あなたの場合、キャストは最初にlongfromに移動しobject次にtoに移動する必要がありulongます。

詳細については、この質問を参照してください。

int を 10 進数としてボックス化解除できないのはなぜですか?

また、Eric Lippert によるブログ投稿へのリンクもあります。

どのように

ご存知のように、1 つの方法はT- にキャストする前に元の型にキャストすることです。もちろん、元の型 T.

Convert.ToUInt64コメントで述べたように、別の方法は、明示的なキャストではなく、変換ルーチン ( ) を使用することです。

これは、以下を使用して実現できる可能性がありますFunc<object, T>

public static T ExecuteScalar<T>(
    Func<object, T> conversionFunctor,
    string query, 
    SqlConnection connection, 
    params SqlParameter[] parameters) where T : new()
{
    // Create SqlCommand
    SqlCommand command = CreateCommand(query, connection, parameters);

    // Execute command using ExecuteScalar
    object result = command.ExecuteScalar();

    // Return value as expected type
    if (result == null || result is DBNull) 
        return default(T);

    return conversionFunctor(result);
}

電話をかける:

ulong minActiveRowversion = 
    SqlUtils.ExecuteScalar<ulong>(
        Convert.ToUInt64,
        "SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
        , _connectionString);
于 2012-06-27T12:42:25.547 に答える
4

Adam の回答は、問題を正しく特定しています。Tここに解決策があります。組み込みまたはカスタム変換でキャストできる限り、LINQ を使用して任意の型をアンボックスできます。

static T UnboxUnchecked<T>(object obj) {
    var pe = Expression.Parameter(typeof(object));
    return Expression.Lambda<Func<object,T>>(
        Expression.Convert(
            Expression.Convert(pe, obj.GetType())
        ,   typeof (T)
        )
    ,   pe
    ).Compile()(obj);
}

このメソッドは、最初にオブジェクトを実際の型にボックス化解除してから変換を適用する LINQ 式を生成します。メソッドの最後の行を置き換えます

return (T)result;

return UnboxUnchecked<T>(result);

それを機能させるために。

コンパイルされたラムダをキャッシュすることで、この種の変換をより効率的にする方法を説明する記事へのリンクを次に示します。

于 2012-06-27T13:01:24.783 に答える