扩展 Jon Skeet 对This Previous Question的回答。Skeet 没有解决当负值和二进制补码值进入图片时发生的故障。
简而言之,我想将任何简单类型(保存在一个未知的 boxed 中object
)转换为,System.UInt64
以便我可以使用底层的二进制表示。
我为什么要这样做?请参阅底部的说明。
下面的示例显示了Convert.ToInt64(object)
和Convert.ToUInt64(object)
都中断 ( OverflowException
) 的情况。
造成以下情况的原因只有两个OverflowExceptions
:
-10UL
转换为时会导致异常,Int64
因为负值强制转换为0xfffffffffffffff6
(在未经检查的上下文中),这是一个大于Int64.MaxValue
. 我希望将其转换为-10L
.转换为 时
UInt64
,持有负值的有符号类型会导致异常,因为-10
小于UInt64.MinValue
。我希望这些转换为它们真正的二进制补码值(即0xffffffffffffffff6
)。无符号类型并不真正持有负值-10
,因为它在未经检查的上下文中被转换为二进制补码;因此,无符号类型不会发生异常。
kludge 解决方案似乎是转换为,Int64
然后是未经检查的转换为UInt64
. 这种中间转换会更容易,因为Int64
在直接转换为UInt64
.
注意:该示例使用unchecked
上下文仅用于在装箱期间将负值强制转换为无符号类型(这会创建正的二进制补码等效值)。这种unchecked
背景不是当前问题的一部分。
using System;
enum DumbEnum { Negative = -10, Positive = 10 };
class Test
{
static void Main()
{
unchecked
{
Check((sbyte)10);
Check((byte)10);
Check((short)10);
Check((ushort)10);
Check((int)10);
Check((uint)10);
Check((long)10);
Check((ulong)10);
Check((char)'\u000a');
Check((float)10.1);
Check((double)10.1);
Check((bool)true);
Check((decimal)10);
Check((DumbEnum)DumbEnum.Positive);
Check((sbyte)-10);
Check((byte)-10);
Check((short)-10);
Check((ushort)-10);
Check((int)-10);
Check((uint)-10);
Check((long)-10);
//Check((ulong)-10); // OverflowException
Check((float)-10);
Check((double)-10);
Check((bool)false);
Check((decimal)-10);
Check((DumbEnum)DumbEnum.Negative);
CheckU((sbyte)10);
CheckU((byte)10);
CheckU((short)10);
CheckU((ushort)10);
CheckU((int)10);
CheckU((uint)10);
CheckU((long)10);
CheckU((ulong)10);
CheckU((char)'\u000a');
CheckU((float)10.1);
CheckU((double)10.1);
CheckU((bool)true);
CheckU((decimal)10);
CheckU((DumbEnum)DumbEnum.Positive);
//CheckU((sbyte)-10); // OverflowException
CheckU((byte)-10);
//CheckU((short)-10); // OverflowException
CheckU((ushort)-10);
//CheckU((int)-10); // OverflowException
CheckU((uint)-10);
//CheckU((long)-10); // OverflowException
CheckU((ulong)-10);
//CheckU((float)-10.1); // OverflowException
//CheckU((double)-10.1); // OverflowException
CheckU((bool)false);
//CheckU((decimal)-10); // OverflowException
//CheckU((DumbEnum)DumbEnum.Negative); // OverflowException
}
}
static void Check(object o)
{
Console.WriteLine("Type {0} converted to Int64: {1}",
o.GetType().Name, Convert.ToInt64(o));
}
static void CheckU(object o)
{
Console.WriteLine("Type {0} converted to UInt64: {1}",
o.GetType().Name, Convert.ToUInt64(o));
}
}
为什么?
为什么我希望能够在所有这些值类型之间相互转换UInt64
?因为我编写了一个类库,可以将结构或类转换为打包成单个UInt64
值的位字段。
示例:考虑DiffServ
每个 IP 数据包头中的字段,该字段由许多二进制位字段组成:
使用我的类库,我可以创建一个结构来表示 DiffServ 字段。我创建了一个BitFieldAttribute
指示哪些位属于二进制表示中的位置:
struct DiffServ : IBitField
{
[BitField(3,0)]
public PrecedenceLevel Precedence;
[BitField(1,3)]
public bool Delay;
[BitField(1,4)]
public bool Throughput;
[BitField(1,5)]
public bool Reliability;
[BitField(1,6)]
public bool MonetaryCost;
}
enum PrecedenceLevel
{
Routine, Priority, Immediate, Flash, FlashOverride, CriticEcp,
InternetworkControl, NetworkControl
}
然后,我的类库可以将此结构的实例与其正确的二进制表示形式相互转换:
// Create an arbitrary DiffServe instance.
DiffServ ds = new DiffServ();
ds.Precedence = PrecedenceLevel.Immediate;
ds.Throughput = true;
ds.Reliability = true;
// Convert struct to value.
long dsValue = ds.Pack();
// Create struct from value.
DiffServ ds2 = Unpack<DiffServ>(0x66);
为了实现这一点,我的类库会查找用BitFieldAttribute
. 获取和设置成员检索包含装箱值类型(int、bool、enum 等)的对象。因此,我需要拆箱任何值类型并将其转换为其准系统二进制表示,以便可以提取和打包位成一个UInt64
值。