C# 类型推断得到错误的类型

发布于 2024-08-07 01:04:30 字数 1053 浏览 3 评论 0原文

我创建了以下属性,如果在 ViewState[TOTAL_RECORD_COUNT]null 时访问 getter,则会引发 InvalidCastException

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

我的想法是,它错误地尝试将 ViewState[TOTAL_RECORD_COUNT] 中的对象拆箱为 int,但失败了,因为它包含 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' 作为“long”,如果我执行 (ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name 我得到 Int32。我可以执行 (long)-1 并最终得到 -1 作为 Int64...那么怎么了?

I created the following property, which threw an InvalidCastException if the getter was accessed when ViewState[TOTAL_RECORD_COUNT] was null.

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

My thought is that it incorrectly attempted to unbox the object in ViewState[TOTAL_RECORD_COUNT] to an int, which failed because it contained a long, but I think there might be a flaw in that logic. I will leave it as an exercise to the reader to point out that flaw.

I have since changed that property to read

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

which works just swell. Still, I am left wondering what was wrong with my original version... StackOverflow to the rescue?

Note that if i try to execute (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1) in the Immediate Window, I get the error message Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long' and if I execute (ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name I get Int32. I can execute (long)-1 and end up with -1 as an Int64...so what's up?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

最后的乘客 2024-08-14 01:04:30

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

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

它必须推断出表达式 o ?? 的结果类型。 -1 不知何故。左边是一个对象,右边是一个int。显然,该表达式最通用的类​​型也是object。但是,这意味着如果它实际上最终使用了 -1 (因为 o 为 null),则必须将其转换为 object - 对于 int 来说,这意味着拳击。

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

long y = (long)x;

由于xobject,所以这是拆箱。但是,您只能将值类型拆箱为完全相同的类型(唯一的例外是您可以用有符号类型替换等效的无符号类型,并用枚举替换其基础基类型)。也就是说,您无法将 int 拆箱为 long。重现此问题的一种更简单的方法(无需“额外”代码)是:

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

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

The return type of ViewState indexer is Object (I assume you mean ASP.NET viewstate here). Now consider what the compiler has to do when it sees this (which is equivalent to your code):

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

It has to deduce the result type of expression o ?? -1 somehow. On the left it sees an object, on the right is an int. Clearly, the most general type for this expression is also object. However, this means that if it actually ends up using that -1 (because o was null), it will have to convert it to object - and for an int, this means boxing.

So x is of type object, and it may contain an int (and maybe also some other integral type - we don't know what is in your viewstate, it could be short, for example). Now you write:

long y = (long)x;

Since x is object, this is unboxing. However, you can only unbox value types into exact same type (with the only exception being that you can substitute a signed type for an equivalent unsigned type, and enum for its underlying base type). That is, you cannot unbox int into long. A far simpler way to repro this, with no "extra" code, would be:

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

Which also throws InvalidCastException, and for exact same reason.

独夜无伴 2024-08-14 01:04:30

演员阵容必须只有一步。

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

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

A cast has to be one step only.

The expression <object> ?? <int> will produce another object, and when the first value is null, ie. ViewState[TOTAL_RECORD_COUNT] is null, then the resulting value will be an object, with a boxed Int32 in it.

Since you cannot unbox an object containing an Int32 to a long, you need to first unbox it to an Int32, and then cast it to a long.

葮薆情 2024-08-14 01:04:30

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

   ViewState[TOTAL_RECORD_COUNT] ?? -1

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

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

The problems is not the unboxing of the ViewState[TOTAL_RECORD_COUNT], the problem is the boxing and unboxing of the -1.

   ViewState[TOTAL_RECORD_COUNT] ?? -1

You are using the ?? operator on "object" and "int". The resulting type is "object". This means the -1 will be boxed (as int) when the field does not exist in the view state.

Then your program crashes later when it tries to unbox the (int)-1 as a long.

枕花眠 2024-08-14 01:04:30

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

(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; }
}

In your original, if you break it down, you were doing:

(ViewState[TOTAL_RECORD_COUNT] ?? -1)

The null-coalescing operator (??) is specifially designed to:

to define a default value for a nullable value types as well as reference types.

In your case, you're using it to handle a System.Object, so it's going to take your "-1", treat it as an Int32, and box it into a new System.Object. Then, it tries to unbox the Int32 into a long, which fails, since the cast cannot unbox and change the type in a single step.

You can solve this easily by specifying that your -1 is a long by using the L suffix:

public long TotalRecordCount
{
    get { return (long)(ViewState[TOTAL_RECORD_COUNT] ?? -1L); }
    set { ViewState[TOTAL_RECORD_COUNT] = value; }
}
破晓 2024-08-14 01:04:30

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

Int64 is a value type, so casting null to a value type will always throw an exception (NullReferenceException). And casting an Int32 to Int64 will succeed and will not throw an InvalidCastException.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文