C#中的奇怪现象 - UInt32与Int32

3

背景

我通常使用C#编写嵌入式项目的支持程序。我继承了一个项目,这是一个由我的一个客户使用的程序,用于通过RS-232下载和上传Intel Hex文件到他们的一些板子上。该程序多年来一直为他们工作正常,但是对于我正在处理的新板子,它却无法正常工作,需要进行一些修改。

问题

我打开了项目,并找到了问题所在。新的板子在地址范围0x90000000-0x9007FFFF中产生了一些数据,而C#代码使用了int类型的地址数据;这是有符号的,处理超过0x80000000的地址时出现错误。到目前为止还好。但是我变得自负了。

我决定清理代码,用Int16/UInt16/Int32/UInt32替换所有的“int”,具体取决于使用情况。这花了我很长时间,但我认为这将使代码更清晰易懂,并希望避免任何未来的错误。

结果代码停止工作了。我花了大部分时间,最终发现问题在这一行:

currseg = (HexSegment)hex_segments[HashEntry];

HexSegment类型是一个结构体(是的,结构体,而不是类),

public struct HexSegment
{
    UInt32 Address; // this is the fix I made, both were int
    UInt32 Buf_idx;
}

hex_segments是一个哈希表。

最后,HashEntry被定义为“int”,也转换成了UInt32

在调试器中,我看到了这个:

enter image description here

如果您看不到图片,则HashEntry的值为0x00000000。 如果我要求调试器查看hex_segments[0],我会得到一个具有合法数据的MLV.HexSegment。 如果我要求调试器查看hex_segments[HashEntry],我得到的是null!

HashEntry更改为Int32解决了该问题。

就我而言,作为嵌入式程序员,这是一个很大的谜团。有人能解释为什么会这样吗?


0x08000000(int)还是 0x80000000(uint)? - Silvermind
我认为问题在于你将HashEntry的数据类型从int更改为uint。但是,你没有更改插入HashTable的代码,它仍然是int。如果你要求调试器查看hex_segments [0U],你会得到空值。 - canton7
(顺便提一下,“int”始终与“Int32”相同,“uint”始终与“UInt64”相同。通常做法是使用“int”/“uint”,而不是“Int32”/“UInt32”。) - canton7
HashTable不是一个通用类型。键是一个对象。intuint都是值类型,所以两者都可以使用,但它们是不同的,一个装箱的0的int与一个装箱的0的uint非常不同。没有任何方法可以在它们之间进行转换。我猜你正在插入一个int(它被转换为装箱的int),并尝试使用uint(转换为装箱的uint)获取它。即使它们都是“零”,它们也不相等。 - Flydog57
通用集合使生活变得**更加**不容易出错。 - Flydog57
@Flydog57 "严格来说,Convert类有方法可以在它们之间进行转换。但这与OP的问题毫不相关。" - canton7
1个回答

5

因为Hashtable是按对象而不是按整数进行索引的。

public virtual object this[object key] { get; set; }

如果一个对象使用Int32作为关键字添加,那么它与使用UInt32作为关键字添加的对象是不同的。

看看这个例子:

UInt32 index = 0;
Int32 index2 = 0;
Hashtable t = new Hashtable();

t.Add(index, new { name = "matt" });
t.Add(index2, new { name = "Matt" });

var obj = t[index];
var obj2 = t[index2];

Console.WriteLine(obj);
Console.WriteLine(obj2);

3
补充一点:自C# 2以来的建议是使用Dictionary<UInt32, HexSegment>。因为编译器知道查找总是使用UInt32进行,所以它会确保你不能使用除UInt32之外的任何内容。 - canton7
同时,Hastable的文档表示“基于键的哈希码组织的键/值对集合”。但是,如果您在索引和索引2上调用GetHashCode(),它们都会得到0的结果。因此,我仍然不确定为什么它能够正常工作。 - Justin
1
它们是按哈希码组织的,但其中仍然有一个“Equals”检查,并且一个装箱的int!=一个装箱的uint,即使它们具有相同的哈希码。 - canton7
我认为这就是他们所说的面向对象 :) - Steven Burck
在任何动态类型语言中都会遇到相同的问题。你应该使用提供给你的类型系统来帮助避免这种情况的发生。 - canton7
明白了。我试图让代码“更干净”…HashTable.Adds都是使用HashTable.Count的当前值完成的,这当然错过了我的“清理”(我没有开始修改系统代码),但是使用它的那些行按需要进行了索引。现在,我还将HashEntry定义为“int”,并附带了解释。 - Steven Burck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接