วิธีการใช้งาน Bit Fields
ใน C#: คู่มือที่ครอบคลุม
การจัดการกับโครงสร้างข้อมูลแบบกะทัดรัดอย่างมีประสิทธิภาพเป็นความท้าทายทั่วไปในโปรแกรมมิ่ง โดยเฉพาะเมื่อทำงานกับการสื่อสารข้อมูลหรือโครงสร้างไฟล์ระดับต่ำ ใน C# ขณะที่ไม่มีการสนับสนุนแบบเนทีฟสำหรับ bit fields
เหมือนใน C แต่เราสามารถบรรลุฟังก์ชันการทำงานที่คล้ายกันได้ด้วยการใช้แอตทริบิวต์และการสะท้อน(Reflection) อย่างชาญฉลาด บทความนี้จะพาคุณไปสู่วิธีการที่มีประสิทธิภาพในการใช้งานและจัดการกับ bit fields
ใน C#
ปัญหา
สมมุติว่าคุณมีชุดของโครงสร้างที่ต้องการแสดงข้อมูลที่มีหลายแฟล็กบูลีน (หรือบิต) การใช้วิธีการธรรมดาของ C# อาจทำให้ยุ่งเหยิงและส่งผลให้โค้ดอ่านยาก ตัวอย่างเช่น บิตต่อไปนี้ที่ต้องมีการแสดง:
bit0
- original_or_copybit1
- copyrightbit2
- data_alignment_indicatorbit3
- PES_prioritybit4-bit5
- PES_scrambling controlbit6-bit7
- reserved
จุดประสงค์คือการจับโครงสร้างนี้ในลักษณะที่สะอาดและดูแลรักษาง่าย โดยไม่ต้องพึ่งพาการจัดการบิตด้วยตนเองในทุกครั้ง
วิธีแก้ปัญหา
ขั้นตอนที่ 1: สร้างแอตทริบิวต์ความยาวของ Bitfield
ก่อนอื่น ให้เรากำหนดแอตทริบิวต์ที่กำหนดเองที่เรียกว่า BitfieldLengthAttribute
แอตทริบิวต์นี้จะเก็บความยาวของแต่ละ bit field
นี่คือวิธีที่คุณสามารถตั้งค่าได้:
[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: กำหนดคลาสการแปลงพื้นฐาน
ถัดไปเราต้องการวิธีการแปลงแอตทริบิวต์ของเราให้เป็นการแสดงผลจริงของ bit field
เราสามารถสร้างคลาสสถิตสำหรับตรรกะการแปลง:
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: สร้างโครงสร้างข้อมูลของคุณ
ตอนนี้คุณสามารถสร้างโครงสร้างที่แสดงถึง bit fields
ของคุณได้ นี่คือตัวอย่าง:
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# คุณสามารถเลียนแบบพฤติกรรมของ bit fields
จาก C ขณะที่ยังคงทำให้โค้ดของคุณอ่านและดูแลรักษาง่ายขึ้น วิธีการนี้ไม่เพียงแต่ช่วยลดความซับซ้อนของโค้ด แต่ยังทำให้สนับสนุนโครงสร้างหลายตัวได้ง่ายขึ้นโดยไม่มีการทำซ้ำอย่างมาก ดังนั้นครั้งถัดไปที่คุณต้องทำงานกับการแสดงข้อมูลแบบกะทัดรัด ให้พิจารณาใช้วิธีนี้เพื่อหาคำตอบที่มีประสิทธิภาพและเชิงสุนทรียศาสตร์!