为C#添加位域特性

来源:互联网 发布:淘宝上的跳蛋卫生吗 编辑:程序博客网 时间:2024/06/13 15:20

最近项目中由于要对数据进行压缩,所以产生了为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));        }    }}



0 0