3

This Previous Questionに対する Jon Skeet の回答を拡張します。Skeet は、負の値と 2 の補数が画像に入ったときに発生する障害に対処していません。

つまり、基になるバイナリ表現を操作できるように、単純な型 (不明な boxed に保持されているobject)を変換したいと考えています。System.UInt64

なぜ私はこれをしたいのですか?下部の説明を参照してください。

Convert.ToInt64(object)以下の例は、とConvert.ToUInt64(object)の両方が壊れる場合を示しています( OverflowException)。

以下の 2 つの原因のみが考えられOverflowExceptionsます。

  1. -10ULに変換するときに例外が発生します。これはInt64、負の値が (チェックされていないコンテキストで) にキャストされるためです。0xfffffffffffffff6これは、 より大きい正の数ですInt64.MaxValueこれを に変換したい-10L

  2. に変換するとUInt64、負の値を保持する符号付きの型-10は より小さいため、例外が発生しますUInt64.MinValueこれらを真の 2 の補数値 (つまり ) に変換したい0xffffffffffffffff6-10unsigned 型は、unchecked コンテキストで 2 の補数に変換されるため、実際には負の値を保持しません。したがって、符号なしの型では例外は発生しません。

クラッジの解決策は、 への変換のInt64後に への未チェックのキャストが続くようですUInt64Int64に直接変換する場合、1 つのインスタンスだけが 8 つの失敗に対して例外を引き起こすため、この中間キャストはより簡単ですUInt64

注: この例では、uncheckedボクシング中に負の値を符号なし型に強制する目的でのみコンテキストを使用します (これにより、正の 2 の補数に相当する値が作成されます)。この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, decimal64ビット整数に収まらない非常に大きな値を除く)が排除され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 に答える