Comment implémenter des Champs de bits
en C#: Un guide complet
Gérer efficacement des structures de données compactes est un défi courant en programmation, surtout lorsqu’il s’agit de communication de données ou de structures de fichiers de bas niveau. En C#, bien qu’il n’y ait pas de support natif pour les champs de bits comme en C, nous pouvons atteindre une fonctionnalité similaire par une utilisation astucieuse des attributs et de la réflexion. Cet article de blog vous guidera à travers une méthode efficace d’implémentation et de gestion des champs de bits en C#.
Le Problème
Supposons que vous ayez une série de structures qui doivent représenter des données avec plusieurs indicateurs booléens (ou bits). Utiliser des approches conventionnelles de C# peut devenir contraignant et conduire à un code moins lisible. Prenons, par exemple, les bits suivants qui doivent être représentés :
bit0
- original_ou_copiebit1
- droits_d’auteurbit2
- indicateur_d’alignement_de_donnéesbit3
- priorité_PESbit4-bit5
- contrôle_de_scrambling_PESbit6-bit7
- réservé
L’objectif est de capturer cette structure de manière propre et maintenable sans avoir à recourir à une manipulation manuelle des bits à chaque fois.
La Solution
Étape 1 : Créer un Attribut de Longueur de Champ de Bits
Tout d’abord, nous définirons un attribut personnalisé appelé BitfieldLengthAttribute
. Cet attribut contiendra la longueur de chaque champ de bits. Voici comment vous pouvez le configurer :
[global::System.AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
sealed class BitfieldLengthAttribute : Attribute
{
uint length;
public BitfieldLengthAttribute(uint length)
{
this.length = length;
}
public uint Length { get { return length; } }
}
Étape 2 : Définir une Classe de Conversion Primitive
Ensuite, nous devons trouver un moyen de convertir nos attributs en représentations réelles de champs de bits. Nous pouvons créer une classe statique pour la logique de conversion :
static class PrimitiveConversion
{
public static long ToLong<T>(T t) where T : struct
{
long r = 0;
int offset = 0;
foreach (System.Reflection.FieldInfo f in t.GetType().GetFields())
{
object[] attrs = f.GetCustomAttributes(typeof(BitfieldLengthAttribute), false);
if (attrs.Length == 1)
{
uint fieldLength = ((BitfieldLengthAttribute)attrs[0]).Length;
long mask = (1 << (int)fieldLength) - 1;
r |= ((UInt32)f.GetValue(t) & mask) << offset;
offset += (int)fieldLength;
}
}
return r;
}
}
Étape 3 : Créer Votre Structure de Données
Maintenant, vous pouvez créer une structure représentant vos champs de bits. Voici un exemple :
struct PESHeader
{
[BitfieldLength(2)]
public uint réservé;
[BitfieldLength(2)]
public uint contrôle_de_scrambling;
[BitfieldLength(1)]
public uint priorité;
[BitfieldLength(1)]
public uint indicateur_d'alignement_de_données;
[BitfieldLength(1)]
public uint droits_d'auteur;
[BitfieldLength(1)]
public uint original_ou_copie;
}
Étape 4 : Exemple d’Utilisation
Rassemblons tout cela dans un programme simple qui utilise la structure ci-dessus et la méthode de conversion :
public class MainClass
{
public static void Main(string[] args)
{
PESHeader p = new PESHeader
{
réservé = 3,
contrôle_de_scrambling = 2,
indicateur_d'alignement_de_données = 1
};
long l = PrimitiveConversion.ToLong(p);
for (int i = 63; i >= 0; i--)
{
Console.Write(((l & (1L << i)) > 0) ? "1" : "0");
}
Console.WriteLine();
}
}
Réflexions Finales
En utilisant des attributs personnalisés et la réflexion en C#, vous pouvez mimer le comportement des champs de bits
de C tout en gardant votre code lisible et maintenable. Cette approche réduit non seulement la complexité du code, mais rend également plus facile le support de plusieurs structures sans duplication significative des efforts.
La prochaine fois que vous aurez besoin de travailler avec des représentations de données compactes, envisagez d’utiliser cette méthode pour une solution efficace et élégante !