كيفية تنفيذ حقول البت في C#: دليل شامل

إدارة هياكل البيانات المضغوطة بكفاءة هي تحدٍ شائع في البرمجة، خاصة عند العمل مع اتصالات البيانات أو هياكل الملفات منخفضة المستوى. في C#، على الرغم من عدم وجود دعم أصلي لحقول البت كما هو الحال في C، يمكننا تحقيق وظيفة مشابهة من خلال الاستخدام الذكي للسمات والانعكاس. ستوجهك هذه التدوينة عبر طريقة فعالة لتنفيذ وإدارة حقول البت في C#.

المشكلة

افترض أن لديك سلسلة من الهياكل التي تحتاج إلى تمثيل بيانات تحتوي على عدة علامات بوليانية (أو بتات). يمكن أن تصبح الطرق التقليدية في C# مرهقة وتؤدي إلى رمز أقل قابلية للقراءة. خذ، على سبيل المثال، البتات التالية التي تحتاج إلى تمثيلها:

  • bit0 - الأصل_أو_النسخة
  • bit1 - حقوق الطبع والنشر
  • bit2 - مؤشر محاذاة البيانات
  • bit3 - أولوية PES
  • bit4-bit5 - التحكم في تشويش PES
  • bit6-bit7 - محجوزة

الهدف هو التقاط هذه الهيكلية بطريقة نظيفة وقابلة للصيانة دون اللجوء إلى المعالجة اليدوية للبتات في كل مرة.

الحل

الخطوة 1: إنشاء سمة طول حقل البت

أولاً، سنقوم بتعريف سمة مخصصة تسمى BitfieldLengthAttribute. ستحتفظ هذه السمة بطول كل حقل بت. إليك كيفية إعدادها:

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

الخطوة 2: تعريف فئة تحويل أولية

بعد ذلك، نحتاج إلى طريقة لتحويل سماتنا إلى تمثيلات حقيقية لحقول البت. يمكننا إنشاء فئة ثابتة منطق التحويل:

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

الخطوة 3: إنشاء بنية البيانات الخاصة بك

يمكنك الآن إنشاء بنية تمثل حقول البت الخاصة بك. إليك مثالاً:

struct PESHeader
{
    [BitfieldLength(2)]
    public uint reserved;

    [BitfieldLength(2)]
    public uint scrambling_control;

    [BitfieldLength(1)]
    public uint priority;

    [BitfieldLength(1)]
    public uint data_alignment_indicator;

    [BitfieldLength(1)]
    public uint copyright;

    [BitfieldLength(1)]
    public uint original_or_copy;
}

الخطوة 4: مثال على الاستخدام

دعونا نجمع كل شيء في برنامج بسيط يستخدم الهيكلية وطريقة التحويل أعلاه:

public class MainClass
{
    public static void Main(string[] args)
    {
        PESHeader p = new PESHeader
        {
            reserved = 3,
            scrambling_control = 2,
            data_alignment_indicator = 1
        };

        long l = PrimitiveConversion.ToLong(p);

        for (int i = 63; i >= 0; i--)
        {
            Console.Write(((l & (1L << i)) > 0) ? "1" : "0");
        }

        Console.WriteLine();
    }
}

أفكار نهائية

من خلال استخدام السمات المخصصة والانعكاس في C#، يمكنك محاكاة سلوك حقول البت من C مع الحفاظ على قراءة كودك وصيانته. لا تقلل هذه الطريقة من تعقيد الكود فحسب، بل تجعل أيضًا من الأسهل دعم الهياكل المتعددة دون تكرار كبير للجهود.

لذا في المرة القادمة التي تحتاج فيها للعمل مع تمثيلات بيانات مضغوطة، فكر في استخدام هذه الطريقة كحل فعال وأنيق!