为C#添加位域特性

发表于2018-10-18
评论0 1.5k浏览
最近项目中由于要对数据进行压缩,所以产生了为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

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引