将未知的盒装简单值类型(char,int,ulong等)转换为UInt64

时间:2012-04-05 03:42:59

标签: c#

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

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

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

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

以下OverflowExceptions只有两个原因:

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

  2. 转换为-10L时,包含负值的签名类型会导致异常,因为UInt64小于-10我希望这些转换为真正的二进制补码值(UInt64.MinValue)。无符号类型不能真正保持负值0xffffffffffffffff6,因为它被转换为二进制补码在未经检查的背景下;因此,无符号类型不会发生异常。

  3. kludge解决方案似乎是转换为-10,然后是未经检查的转换为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值的位字段。

    示例:考虑每个IP包头中的UInt64字段,该字段由许多二进制位字段组成:

    IP packet DiffServ field

    使用我的类库,我可以创建一个表示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); 修饰的字段/属性。获取和设置成员检索包含盒装值类型(int,bool,enum等)的对象。因此,我需要取消设置任何值类型并将其转换为它的简单二进制表示,以便可以提取和打包这些位成BitFieldAttribute值。

1 个答案:

答案 0 :(得分:2)

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

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

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

欢迎任何改进。