关于C#:InvalidCastException long to ulong

InvalidCastException long to ulong

我有以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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。奇怪的是……下面的第一个方法调用生成错误,但第二个方法调用工作正常。

方法调用1生成错误:

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

错误:

1
System.InvalidCastException: Specified cast is not valid.

方法调用2工作正常:

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

我不明白这是怎么可能的,因为command.ExecuteScalar()方法的结果是:

1
2
object result       | 1955612
result.GetType()    | {Name ="Int64" FullName ="System.Int64"}
  • 有人能告诉我为什么第一个场景是不可能的,第二个场景是有效的吗?
  • 有人能告诉我如何解决这个问题,这样我就可以使用场景1了。

  • 为什么?

    只能将值类型取消对其原始类型的绑定。在您的案例中,演员组首先需要从object转到long,然后转到ulong

    有关详细信息,请参阅此问题:

    为什么我不能把一个整型取为十进制?

    它还链接了埃里克·利珀特的一篇博客文章。

    怎么

    如你所知,一种方法是在铸造到T之前先铸造到原始类型-当然,除非原始类型是T

    如注释所述,另一种方法是使用转换例程(Convert.ToUInt64)而不是显式转换。

    这可能通过使用Func实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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);
    }

    拨打电话:

    1
    2
    3
    4
    5
    ulong minActiveRowversion =
        SqlUtils.ExecuteScalar<ulong>(
            Convert.ToUInt64,
           "SELECT CAST(MIN_ACTIVE_ROWVERSION() AS BIGINT)"
            , _connectionString);

    Adam的答案正确地识别了问题;这里有一个解决方案:您可以使用Linq取消对任何类型的装箱,只要它可以通过内置或自定义转换转换转换转换为T

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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表达式,该表达式首先将对象取消对其实际类型的绑定,然后应用转换。替换方法的最后一行

    1
    return (T)result;

    具有

    1
    return UnboxUnchecked<T>(result);

    让它发挥作用。

    下面是一篇文章的链接,该文章解释了如何通过缓存编译的lambda来提高这种转换的效率。