Convertir les types de valeur simple en boîte inconnus (char, int, ulong, etc.) en UInt64

Développer la réponse de Jon Skeet à cette question précédente . Skeet ne traite pas de l’échec qui survient lorsque des valeurs négatives et des valeurs de complément à deux entrent dans l’image.

En résumé, je souhaite convertir n’importe quel type simple (contenu dans un object encadré inconnu) en System.UInt64 afin de pouvoir System.UInt64 la représentation binary sous-jacente.

Pourquoi est-ce que je veux faire ça? Voir l’explication en bas.

L’exemple ci-dessous montre les cas où Convert.ToInt64(object) et Convert.ToUInt64(object) cassent tous les deux ( OverflowException ).

Les OverflowExceptions ci-dessous n’ont que deux causes:

  1. -10UL provoque une exception lors de la conversion en Int64 car la valeur négative 0xfffffffffffffff6 en 0xfffffffffffffff6 (dans le contexte non vérifié), ce qui correspond à un nombre positif supérieur à Int64.MaxValue . Je veux que cela convertisse en -10L .

  2. Lors de la conversion en UInt64 , les types signés contenant des valeurs négatives provoquent une exception car -10 est inférieur à UInt64.MinValue . Je veux que ces derniers se convertissent en valeur du complément à leurs deux vrais (qui est 0xffffffffffffffff6 ). Les types non signés ne détiennent pas vraiment la valeur négative -10 car ils sont convertis en complément à deux dans le contexte non contrôlé; ainsi, aucune exception ne se produit avec les types non signés.

La solution kludge semble être une conversion en Int64 suivie d’une conversion non UInt64 en UInt64 . Cette conversion intermédiaire serait plus facile, car une seule instance provoquait une exception pour les échecs Int64 par rapport à huit lors de la conversion directe en UInt64 .

Remarque: L’exemple utilise un contexte unchecked uniquement dans le but de forcer des valeurs négatives dans des types non signés lors de la mise en boîte (ce qui crée une valeur équivalente du complément à deux positif). Ce contexte unchecked ne fait pas partie du problème actuel.

 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)); } } 

POURQUOI?

Pourquoi est-ce que je veux pouvoir convertir tous ces types de valeur en UInt64 ? Parce que j’ai écrit une bibliothèque de classes qui convertit les structures ou les classes en champs de bits regroupés dans une seule valeur UInt64 .

Exemple: considérons le champ DiffServ dans chaque en-tête de paquet IP, composé d’un certain nombre de champs de bits binarys:

Champ DiffServ de paquet IP

À l’aide de ma bibliothèque de classes, je peux créer une structure pour représenter le champ DiffServ. J’ai créé un BitFieldAtsortingbute qui indique quels bits appartiennent où dans la représentation binary:

 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 } 

Ma bibliothèque de classes peut ensuite convertir une instance de cette structure en et à partir de sa représentation binary appropriée:

 // 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(0x66); 

Pour ce faire, ma bibliothèque de classes recherche les champs / propriétés décorés avec BitFieldAtsortingbute . L’obtention et la définition de membres récupère un object contenant le type de valeur encadré (int, bool, enum, etc.). Par conséquent, je dois déballer n’importe quel type de valeur et le convertir en une représentation binary sans système d’exploitation afin que les bits puissent être extraits et compressés. dans une valeur UInt64 .

Je vais poster ma meilleure solution en tant que fourrage pour les masses.

Ces conversions éliminent toutes les exceptions (sauf les très grandes valeurs float, double, decimal qui ne rentrent pas dans les entiers 64 bits) lors de la déballage d’un type de valeur simple inconnu contenu dans l’ object o :

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

Toute amélioration apscope à cela sera la bienvenue.