2

我创建了以下属性,它抛出了一个InvalidCastExceptionif getter was 访问 when ViewState[TOTAL_RECORD_COUNT]is null

public long TotalRecordCount
{
    get { return (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1); }
    set { ViewState[TOTAL_RECORD_COUNT] = value; }
}

我的想法是它错误地尝试将对象拆箱ViewState[TOTAL_RECORD_COUNT]到a 中,但int失败了,因为它包含 a long,但我认为该逻​​辑可能存在缺陷。我将把它作为练习留给读者指出这个缺陷。

从那以后,我将该属性更改为读取

public long TotalRecordCount
{
    get { return (long?)ViewState[TOTAL_RECORD_COUNT] ?? -1; }
    set { ViewState[TOTAL_RECORD_COUNT] = value; }
}

效果很好。不过,我仍然想知道我的原始版本出了什么问题...... StackOverflow 来救援?

请注意,如果我尝试(long)(ViewState[TOTAL_RECORD_COUNT] ?? -1)在即时窗口中执行,我会收到错误消息Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long',如果我执行(ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name,我会收到Int32. 我可以执行(long)-1并最终以 -1 作为Int64...所以怎么了?

4

5 回答 5

13

ViewState索引器的返回类型是Object(我假设您在这里指的是 ASP.NET 视图状态)。现在考虑一下编译器在看到它时必须做什么(这相当于您的代码):

object o = ViewState[...];
var x = o ?? -1;

它必须以o ?? -1某种方式推断出表达式的结果类型。在左边它看到一个object,在右边是一个int。显然,这个表达式最通用的类​​型也是object. 但是,这意味着如果它实际上最终使用它-1(因为o它是 null),它必须将它转换为object- 而对于 an int,这意味着装箱。

x类型也是如此object,它可能包含一个int(可能还有其他一些整数类型 - 我们不知道您的视图状态中有什么short,例如,它可能是)。现在你写:

long y = (long)x;

既然xobject,这是拆箱。但是,您只能将值类型拆箱为完全相同的类型(唯一的例外是您可以将有符号类型替换为等效的无符号类型,并将枚举替换为其基础基类型)。也就是说,您不能拆箱intlong. 一个更简单的方法来重现这个,没有“额外”代码,将是:

object x = 123;
long y = (long)x;

这也会抛出InvalidCastException,并且出于完全相同的原因。

于 2009-10-01T20:18:34.433 回答
5

演员表只能是一个步骤。

该表达式<object> ?? <int>将产生另一个对象,并且当第一个值为空时,即。ViewState[TOTAL_RECORD_COUNT]为 null,则结果值将是一个对象,其中包含一个装箱的 Int32。

由于您无法将包含 Int32 的对象拆箱为 long,因此您需要先将其拆箱为 Int32,然后将其强制转换为 long。

于 2009-10-01T20:11:12.030 回答
1

在你的原作中,如果你把它分解,你正在做:

(ViewState[TOTAL_RECORD_COUNT] ?? -1)

合并运算符 (??)专门设计用于:

为可为空的值类型以及引用类型定义默认值。

在您的情况下,您使用它来处理 System.Object,因此它将采用您的“-1”,将其视为 Int32,并将其装箱到新的 System.Object 中。然后,它尝试将 Int32 拆箱为 long,但失败了,因为强制转换无法在一个步骤中拆箱和更改类型。

您可以通过使用 L 后缀指定您的 -1 为 long 来轻松解决此问题:

public long TotalRecordCount
{
    get { return (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1L); }
    set { ViewState[TOTAL_RECORD_COUNT] = value; }
}
于 2009-10-01T20:06:38.833 回答
1

问题不在于拆箱ViewState[TOTAL_RECORD_COUNT],问题在于 -1 的装箱和拆箱。

   ViewState[TOTAL_RECORD_COUNT] ?? -1

你用的是??“object”和“int”上的运算符。结果类型是“对象”。这意味着当视图状态中不存在该字段时,-1 将被装箱(作为 int)。

然后,当您的程序尝试将 (int)-1 作为 long 拆箱时,您的程序稍后会崩溃。

于 2009-10-01T20:08:59.270 回答
0

Int64是一种值类型,因此转换null为值类型总是会抛出异常 ( NullReferenceException)。并且将 Int32 转换为 Int64 将成功并且不会抛出InvalidCastException.

于 2009-10-01T20:04:46.457 回答