C#中的位字段
时间:2020-03-05 18:40:20 来源:igfitidea点击:
我有一个需要填充并写入磁盘的结构(实际上有几个)。
一个例子是:
byte-6 bit0 - original_or_copy bit1 - copyright bit2 - data_alignment_indicator bit3 - PES_priority bit4-bit5 - PES_scrambling control. bit6-bit7 - reserved
在C语言中,我可能会执行以下操作:
struct PESHeader { unsigned reserved:2; unsigned scrambling_control:2; unsigned priority:1; unsigned data_alignment_indicator:1; unsigned copyright:1; unsigned original_or_copy:1; };
在C中有什么方法可以使我使用结构取消引用点运算符来访问这些位吗?
对于两个结构,我只需要包装访问器函数中的位移即可。
我有大量的结构要以这种方式处理,因此我正在寻找更易于阅读和编写更快的东西。
解决方案
回答
带有Flags属性的枚举可以帮助吗?看这里:
在C#中[Flags]枚举属性是什么意思?
回答
我们想要StructLayoutAttribute
[StructLayout(LayoutKind.Explicit, Size=1, CharSet=CharSet.Ansi)] public struct Foo { [FieldOffset(0)]public byte original_or_copy; [FieldOffset(0)]public byte copyright; [FieldOffset(0)]public byte data_alignment_indicator; [FieldOffset(0)]public byte PES_priority; [FieldOffset(0)]public byte PES_scrambling_control; [FieldOffset(0)]public byte reserved; }
这实际上是一个并集,但我们可以将其用作位域-我们只需要知道每个字段的位应该在字节中的哪个位置即可。实用程序函数和/或者与AND相对的常量会有所帮助。
const byte _original_or_copy = 1; const byte _copyright = 2; //bool ooo = foo.original_or_copy(); static bool original_or_copy(this Foo foo) { return (foo.original_or_copy & _original_or_copy) == original_or_copy; }
还有LayoutKind.Sequential,它可以让我们以C方式进行操作。
回答
通过使用枚举,我们可以做到这一点,但是看起来很尴尬。
[Flags] public enum PESHeaderFlags { IsCopy = 1, // implied that if not present, then it is an original IsCopyrighted = 2, IsDataAligned = 4, Priority = 8, ScramblingControlType1 = 0, ScramblingControlType2 = 16, ScramblingControlType3 = 32, ScramblingControlType4 = 16+32, ScramblingControlFlags = ScramblingControlType1 | ScramblingControlType2 | ... ype4 etc. }
回答
我认为,如果将它设为字节枚举,那么标志枚举也可以工作:
[Flags] enum PesHeaders : byte { /* ... */ }
回答
我可能会使用属性将某些东西组合在一起,然后使用一个转换类将适当地归因于结构的结构转换为位域基元。就像是...
using System; namespace BitfieldTest { [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; } } } static class PrimitiveConversion { public static long ToLong<T>(T t) where T : struct { long r = 0; int offset = 0; // For every field suitably attributed with a BitfieldLength 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; // Calculate a bitmask of the desired length long mask = 0; for (int i = 0; i < fieldLength; i++) mask |= 1 << i; r |= ((UInt32)f.GetValue(t) & mask) << offset; offset += (int)fieldLength; } } return r; } } 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; }; public class MainClass { public static void Main(string[] args) { PESHeader p = new PESHeader(); p.reserved = 3; p.scrambling_control = 2; p.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(); return; } } }
产生预期的... 000101011. 当然,它需要更多的错误检查和更精巧的键入,但是(我认为)该概念是合理的,可重用的,并且可以让我们轻松打出一打维护的结构。
亚当