简体   繁体   English

C#类型推断获取错误的类型

[英]C# Type Inference Gets the Wrong Type

I created the following property, which threw an InvalidCastException if the getter was accessed when ViewState[TOTAL_RECORD_COUNT] was null . 我创建了以下属性,如果在ViewState[TOTAL_RECORD_COUNT]null时访问了getter,则会抛出InvalidCastException

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. 我的想法是它错误地尝试将ViewState[TOTAL_RECORD_COUNT]的对象取消装入一个int ,因为它包含一个long而失败,但我认为这个逻辑可能存在缺陷。 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? 不过,我仍然想知道我的原始版本有什么问题... StackOverflow救援?

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' 请注意,如果我尝试在立即窗口中执行(long)(ViewState[TOTAL_RECORD_COUNT] ?? -1) ,我收到错误消息Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long' Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long' and if I execute (ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name I get Int32 . Cannot unbox 'ViewState[TOTAL_RECORD_COUNT] ?? -1' as a 'long' ,如果我执行(ViewState[TOTAL_RECORD_COUNT] ?? -1).GetType().Name我得到Int32 I can execute (long)-1 and end up with -1 as an Int64 ...so what's up? 我可以执行(long)-1并以-1作为Int64 ...所以这是什么?

The return type of ViewState indexer is Object (I assume you mean ASP.NET viewstate here). ViewState索引器的返回类型是Object (我假设你的意思是ASP.NET viewstate)。 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 它必须推导出表达式的结果类型o ?? -1 o ?? -1 somehow. o ?? -1不知何故。 On the left it sees an object , on the right is an int . 在左边它看到一个object ,在右边是一个int Clearly, the most general type for this expression is also object . 显然,这个表达式的最常见类型也是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. 但是,这意味着如果它实际上最终使用-1 (因为o为null),则必须将其转换为object - 对于int ,这意味着装箱。

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). 所以xobject类型,它可能包含一个int (也许还有一些其他整数类型 - 我们不知道你的viewstate中有什么,例如它可能很short )。 Now you write: 现在你写:

long y = (long)x;

Since x is object , this is unboxing. 由于xobject ,因此这是拆箱。 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 . 也就是说,你不能将unbox int转换为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. 这也会抛出InvalidCastException ,并且出于同样的原因。

A cast has to be one step only. 演员必须只是一步。

The expression <object> ?? <int> 表达式<object> ?? <int> <object> ?? <int> will produce another object, and when the first value is null, ie. <object> ?? <int>将产生另一个对象,当第一个值为null时,即。 ViewState[TOTAL_RECORD_COUNT] is null, then the resulting value will be an object, with a boxed Int32 in it. ViewState[TOTAL_RECORD_COUNT]为null,然后结果值将是一个对象,其中包含一个带盒的Int32。

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. 由于无法将包含Int32的对象解包为long,因此需要首先将其解包到Int32,然后将其转换为long。

In your original, if you break it down, you were doing: 在你原版中,如果你把它分解,你就是这么做的:

(ViewState[TOTAL_RECORD_COUNT] ?? -1)

The null-coalescing operator (??) is specifially designed to: null-coalescing运算符(??)专门设计用于:

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. 在您的情况下,您正在使用它来处理System.Object,因此它将采用您的“-1”,将其视为Int32,并将其装入新的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. 然后,它尝试将Int32解包为long,这会失败,因为强制转换不能取消装箱并在一个步骤中更改类型。

You can solve this easily by specifying that your -1 is a long by using the L suffix: 您可以通过使用L后缀指定-1为long来轻松解决此问题:

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

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]的取消装箱ViewState[TOTAL_RECORD_COUNT] ,问题是-1的装箱和拆箱。

   ViewState[TOTAL_RECORD_COUNT] ?? -1

You are using the ?? 你正在使用?? operator on "object" and "int". “对象”和“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. 这意味着当视图状态中不存在该字段时,-1将被装箱(作为int)。

Then your program crashes later when it tries to unbox the (int)-1 as a long. 然后,当程序尝试将(int)-1解包为long时,程序会崩溃。

Int64 is a value type, so casting null to a value type will always throw an exception ( NullReferenceException ). Int64是一个值类型,因此将null转换为值类型将始终抛出异常( NullReferenceException )。 And casting an Int32 to Int64 will succeed and will not throw an InvalidCastException . 并且将Int32转换为Int64将成功,并且不会抛出InvalidCastException

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM