3

扩展 Jon Skeet 对This Previous Question的回答。Skeet 没有解决当负值和二进制补码值进入图片时发生的故障。

简而言之,我想将任何简单类型(保存在一个未知的 boxed 中object)转换为,System.UInt64以便我可以使用底层的二进制表示。

我为什么要这样做?请参阅底部的说明。

下面的示例显示了Convert.ToInt64(object)Convert.ToUInt64(object)都中断 ( OverflowException) 的情况。

造成以下情况的原因只有两个OverflowExceptions

  1. -10UL转换为时会导致异常,Int64因为负值强制转换为0xfffffffffffffff6(在未经检查的上下文中),这是一个大于Int64.MaxValue. 我希望将其转换为-10L.

  2. 转换为 时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 数据包头中的字段,该字段由许多二进制位字段组成:

IP 数据包 DiffServ 字段

使用我的类库,我可以创建一个结构来表示 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值。

4

1 回答 1

2

我将发布我最好的解决方案作为大众的饲料。

这些转换在取消装箱中包含的未知简单值类型时消除了所有异常(除了float, double, decimal不适合 64 位整数的非常大的值)object o

long l = o is ulong ? (long)(ulong)o : Convert.ToInt64(o));
ulong u = o is ulong ? (ulong)o : (ulong)Convert.ToInt64(o));

对此的任何改进都将受到欢迎。

于 2012-04-05T04:22:38.807 回答