This Previous Questionに対する Jon Skeet の回答を拡張します。Skeet は、負の値と 2 の補数が画像に入ったときに発生する障害に対処していません。
つまり、基になるバイナリ表現を操作できるように、単純な型 (不明な boxed に保持されているobject
)を変換したいと考えています。System.UInt64
なぜ私はこれをしたいのですか?下部の説明を参照してください。
Convert.ToInt64(object)
以下の例は、とConvert.ToUInt64(object)
の両方が壊れる場合を示しています( OverflowException
)。
以下の 2 つの原因のみが考えられOverflowExceptions
ます。
-10UL
に変換するときに例外が発生します。これはInt64
、負の値が (チェックされていないコンテキストで) にキャストされるためです。0xfffffffffffffff6
これは、 より大きい正の数ですInt64.MaxValue
。これを に変換したい-10L
。に変換すると
UInt64
、負の値を保持する符号付きの型-10
は より小さいため、例外が発生しますUInt64.MinValue
。これらを真の 2 の補数値 (つまり ) に変換したい0xffffffffffffffff6
。-10
unsigned 型は、unchecked コンテキストで 2 の補数に変換されるため、実際には負の値を保持しません。したがって、符号なしの型では例外は発生しません。
クラッジの解決策は、 への変換のInt64
後に への未チェックのキャストが続くようですUInt64
。Int64
に直接変換する場合、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 パケット ヘッダーのフィールドを考えてみましょう。
クラス ライブラリを使用して、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
。