为C#添加位域特性
发表于2018-10-18
最近项目中由于要对数据进行压缩,所以产生了为C#添加类似C++中的位域的特性,网上已经有些内容了,但是感觉还不是很好用,所有自己写了一个关于为C#添加位域特性的内容出来,分享给大家。
先看用法:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using GameData; namespace ConsoleApplication5 { class MyTest01 : BitField { [BitInfo(3)] public bool d0; [BitInfo(3)] public short d1; [BitInfo(3)] public int d2; [BitInfo(3)] public int d3; [BitInfo(3)] public int d4; [BitInfo(3)] public int d5; public MyTest01(bool _d0, short _d1, int _d2, int _d3, int _d4, int _d5) { d0 = _d0; d1 = _d1; d2 = _d2; d3 = _d3; d4 = _d4; d5 = _d5; } public MyTest01(byte[] datas) { parse(datas); } public new string ToString() { return string.Format("d0: {0}, d1: {1}, d2: {2}, d3: {3}, d4: {4}, d5: {5} \r\nbinary => {6}", d0, d1, d2, d3, d4, d5, ArrayConverter.toBinary(toArray())); } }; class MyTest02 : BitField { [BitInfo(5)] public bool val0; [BitInfo(5)] public byte val1; [BitInfo(15)] public uint val2; [BitInfo(15)] public float val3; [BitInfo(15)] public int val4; [BitInfo(15)] public int val5; [BitInfo(15)] public int val6; public MyTest02(bool v0, byte v1, uint v2, float v3, int v4, int v5, int v6) { val0 = v0; val1 = v1; val2 = v2; val3 = v3; val4 = v4; val5 = v5; val6 = v6; } public MyTest02(byte[] datas) { parse(datas); } public new string ToString() { return string.Format("val0: {0}, val1: {1}, val2: {2}, val3: {3}, val4: {4}, val5: {5}, val6: {6}\r\nbinary => {7}", val0, val1, val2, val3, val4, val5, val6, ArrayConverter.toBinary(toArray())); } } public class MainClass { public static void Main(string[] args) { MyTest01 p = new MyTest01(false, 1, 2, 3, -1, -2); Debug.Log("P:: " + p.ToString()); MyTest01 p2 = new MyTest01(p.toArray()); Debug.Log("P2:: " + p2.ToString()); MyTest02 t = new MyTest02(true, 1, 12, -1.3f, 4, -5, 100); Debug.Log("t:: " + t.ToString()); MyTest02 t2 = new MyTest02(t.toArray()); Debug.Log("t:: " + t.ToString()); Console.Read(); return; } } }
输出:
代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GameData { [global::System.AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] public sealed class BitInfoAttribute : Attribute { byte length; public BitInfoAttribute(byte length) { this.length = length; } public byte Length { get { return length; } } } public abstract class BitField { public void parse<T>(T[] vals) { analysis().parse(this, ArrayConverter.convert<T, uint>(vals)); } public byte[] toArray() { return ArrayConverter.convert<uint, byte>(analysis().toArray(this)); } public T[] toArray<T>() { return ArrayConverter.convert<uint, T>(analysis().toArray(this)); } static Dictionary<Type, BitTypeInfo> bitInfoMap = new Dictionary<Type, BitTypeInfo>(); private BitTypeInfo analysis() { Type type = this.GetType(); if (!bitInfoMap.ContainsKey(type)) { List<BitInfo> infos = new List<BitInfo>(); byte dataIdx = 0, offset = 0; foreach (System.Reflection.FieldInfo f in type.GetFields()) { object[] attrs = f.GetCustomAttributes(typeof(BitInfoAttribute), false); if (attrs.Length == 1) { byte bitLen = ((BitInfoAttribute)attrs[0]).Length; if (offset + bitLen > 32) { dataIdx++; offset = 0; } infos.Add(new BitInfo(f, bitLen, dataIdx, offset)); offset += bitLen; } } bitInfoMap.Add(type, new BitTypeInfo(dataIdx + 1, infos.ToArray())); } return bitInfoMap[type]; } } class BitTypeInfo { public int dataLen { get; private set; } public BitInfo[] bitInfos { get; private set; } public BitTypeInfo(int _dataLen, BitInfo[] _bitInfos) { dataLen = _dataLen; bitInfos = _bitInfos; } public uint[] toArray<T>(T obj) { uint[] datas = new uint[dataLen]; foreach (BitInfo bif in bitInfos) { bif.encode(obj, datas); } return datas; } public void parse<T>(T obj, uint[] vals) { foreach (BitInfo bif in bitInfos) { bif.decode(obj, vals); } } } class BitInfo { private System.Reflection.FieldInfo field; private uint mask; private byte idx, offset, shiftA, shiftB; private bool isUnsigned = false; public BitInfo(System.Reflection.FieldInfo _field, byte _bitLen, byte _idx, byte _offset) { field = _field; mask = (uint)(((1 << _bitLen) - 1) << _offset); idx = _idx; offset = _offset; shiftA = (byte)(32 - _offset - _bitLen); shiftB = (byte)(32 - _bitLen); if (_field.FieldType == typeof(bool) || _field.FieldType == typeof(byte) || _field.FieldType == typeof(char) || _field.FieldType == typeof(uint) || _field.FieldType == typeof(ulong) || _field.FieldType == typeof(ushort)) { isUnsigned = true; } } public void encode(Object obj, uint[] datas) { if (isUnsigned) { uint val = (uint)Convert.ChangeType(field.GetValue(obj), typeof(uint)); datas[idx] |= ((uint)(val << offset) & mask); } else { int val = (int)Convert.ChangeType(field.GetValue(obj), typeof(int)); datas[idx] |= ((uint)(val << offset) & mask); } } public void decode(Object obj, uint[] datas) { if (isUnsigned) { field.SetValue(obj, Convert.ChangeType((((uint)(datas[idx] & mask)) << shiftA) >> shiftB, field.FieldType)); } else { field.SetValue(obj, Convert.ChangeType((((int)(datas[idx] & mask)) << shiftA) >> shiftB, field.FieldType)); } } } public class ArrayConverter { public static T[] convert<T>(uint[] val) { return convert<uint, T>(val); } public static T1[] convert<T0, T1>(T0[] val) { T1[] rt = null; // type is same or length is same // refer to http://stackoverflow.com/questions/25759878/convert-byte-to-sbyte if (typeof(T0) == typeof(T1)) { rt = (T1[])(Array)val; } else { int len = Buffer.ByteLength(val); int w = typeWidth<T1>(); if (w == 1) { // bool rt = new T1[len * 8]; } else if (w == 8) { rt = new T1[len]; } else { // w > 8 int nn = w / 8; int len2 = (len / nn) + ((len % nn) > 0 ? 1 : 0); rt = new T1[len2]; } Buffer.BlockCopy(val, 0, rt, 0, len); } return rt; } public static string toBinary<T>(T[] vals) { StringBuilder sb = new StringBuilder(); int width = typeWidth<T>(); int len = Buffer.ByteLength(vals); for (int i = len-1; i >=0; i--) { sb.Append(Convert.ToString(Buffer.GetByte(vals, i), 2).PadLeft(8, '0')).Append(" "); } return sb.ToString(); } private static int typeWidth<T>() { int rt = 0; if (typeof(T) == typeof(bool)) { // x rt = 1; } else if (typeof(T) == typeof(byte)) { // x rt = 8; } else if (typeof(T) == typeof(sbyte)) { rt = 8; } else if (typeof(T) == typeof(ushort)) { // x rt = 16; } else if (typeof(T) == typeof(short)) { rt = 16; } else if (typeof(T) == typeof(char)) { rt = 16; } else if (typeof(T) == typeof(uint)) { // x rt = 32; } else if (typeof(T) == typeof(int)) { rt = 32; } else if (typeof(T) == typeof(float)) { rt = 32; } else if (typeof(T) == typeof(ulong)) { // x rt = 64; } else if (typeof(T) == typeof(long)) { rt = 64; } else if (typeof(T) == typeof(double)) { rt = 64; } else { throw new Exception("Unsupport type : " + typeof(T).Name); } return rt; } } }
另外Unity的Debug在控制台工程中的模拟:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace UnityEngine { class Debug { public static void Log(string msg) { Console.WriteLine(msg); } public static void LogFormat(string msg, params Object[] param) { Console.WriteLine(string.Format(msg, param)); } } }
来自:https://blog.csdn.net/stalendp/article/details/51813348