private void Process(object obj, Type type)
        {
            foreach (MemberInfo member in GetMembers(type))
            {
                BinaryAttribute attr = member.GetCustomAttribute <BinaryAttribute>();

                bool ignoreRead  = !_toBytes && attr.IgnoreOnRead;
                bool ignoreWrite = _toBytes && attr.IgnoreOnWrite;
                if (ignoreRead || ignoreWrite)
                {
                    continue;
                }

                object value;

                switch (member)
                {
                case FieldInfo info:
                    value = Convert(info.FieldType, info.Name, attr, _toBytes ? info.GetValue(obj) : null);
                    if (!_toBytes)
                    {
                        info.SetValue(obj, value);
                    }

                    break;

                case PropertyInfo info:
                    if (_toBytes && !info.CanRead)
                    {
                        continue;
                    }
                    value = Convert(info.PropertyType, info.Name, attr, _toBytes ? info.GetValue(obj) : null);
                    if (!_toBytes && info.CanWrite)
                    {
                        info.SetValue(obj, value);
                    }

                    break;
                }
            }
        }
        private object Convert(Type type, string name, BinaryAttribute attr, object ret)
        {
            IEnumerable <byte> bytes;

            int len = (int)attr.Count;

            bool reverse = _isBigEndian;

            if (attr.Reverse)
            {
                reverse = !reverse;
            }

            if (!_toBytes && attr.Skip > 0)
            {
                _data.Skip((int)attr.Skip);
            }

            switch (attr.CustomType)
            {
            case CustomType.Hex:
                if (_types[type] != CoreType.String)
                {
                    throw new ArgumentException($"Тип поля {name} не является типом string");
                }

                if (!_toBytes)
                {
                    return(BitConverter.ToString(_data.ToArray(len, attr.Reverse)).Replace("-", string.Empty));
                }

                string hex    = (string)ret ?? string.Empty;
                int    hexLen = Math.Max(hex.Length, len * 2);
                if (hexLen % 2 != 0)
                {
                    hexLen++;
                }
                if (hex.Length < hexLen)
                {
                    hex = hex.PadLeft(hexLen, '0');
                }
                bytes = Enumerable.Range(0, hex.Length)
                        .Where(x => x % 2 == 0)
                        .Select(x => System.Convert.ToByte(hex.Substring(x, 2), 16));
                using (IEnumerator <byte> en = bytes.GetEnumerator())
                    _result.AddRange(en.ToArray(len, attr.Reverse));
                return(null);
            }

            if (type.IsEnum)
            {
                int value = 0;
                if (_toBytes)
                {
                    value = (int)ret;
                }

                switch (len)
                {
                case 0:
                case 1:
                    if (_toBytes)
                    {
                        _result.Add((byte)value);
                        return(null);
                    }

                    value = _data.Take(1).Single();
                    break;

                case 2:
                    if (_toBytes)
                    {
                        bytes = BitConverter.GetBytes((short)value);
                        _result.AddRange(reverse ? bytes.Reverse() : bytes);
                        return(null);
                    }

                    value = BitConverter.ToInt16(_data.ToArray(2, reverse), 0);
                    break;

                case 4:
                    if (_toBytes)
                    {
                        bytes = BitConverter.GetBytes(value);
                        _result.AddRange(reverse ? bytes.Reverse() : bytes);
                        return(null);
                    }

                    value = BitConverter.ToInt32(_data.ToArray(4, reverse), 0);
                    break;

                default:
                    throw new ArgumentException(
                              $"Атрибут Count у поля {name} типа {type.Name} может принимать только значения 1, 2 и 4");
                }

                return(Enum.IsDefined(type, value) ? value : 0);
            }

            if (!_types.TryGetValue(type, out CoreType coreType))
            {
                throw new ArgumentException($"Неподдерживаемый тип {type.Name} у поля {name}");
            }

            switch (coreType)
            {
            case CoreType.Boolean:
                if (!_toBytes)
                {
                    return(BitConverter.ToBoolean(_data.ToArray(1), 0));
                }

                _result.AddRange(BitConverter.GetBytes((bool)ret));
                return(null);

            case CoreType.String:
                Encoding encoding;
                switch (attr.Encoding)
                {
                case TextEncoding.Ascii:
                    encoding = Encoding.ASCII;
                    break;

                case TextEncoding.WinRus:
                    encoding = Encoding.GetEncoding(1251);
                    break;

                case TextEncoding.DosRus:
                    encoding = Encoding.GetEncoding(866);
                    break;

                case TextEncoding.Koi8Rus:
                    encoding = Encoding.GetEncoding(20866);
                    break;

                case TextEncoding.Utf8:
                    encoding = Encoding.UTF8;
                    break;

                case TextEncoding.Unicode:
                    encoding = Encoding.Unicode;
                    break;

                case TextEncoding.UnicodeBigEndian:
                    encoding = Encoding.BigEndianUnicode;
                    break;

                default:
                    encoding = Encoding.UTF8;
                    break;
                }

                byte[] val;

                if (!_toBytes)
                {
                    val = _data.ToArray(len, attr.Reverse);
                    if (val.All(b => b == 0))
                    {
                        val = Array.Empty <byte>();
                    }

                    return(encoding.GetString(val).TrimEnd('\0'));
                }

                if (ret == null)
                {
                    ret = string.Empty;
                }
                val = encoding.GetBytes((string)ret);
                using (IEnumerator <byte> en = val.Length < len
                        ? val.Concat(Enumerable.Repeat <byte>(0x20, len - val.Length)).GetEnumerator()
                        : ((IEnumerable <byte>)val).GetEnumerator())
                    _result.AddRange(en.ToArray(len > 0 ? len : val.Length, attr.Reverse));
                return(null);

            case CoreType.ByteArray:
                // TODO Есть ли смысл поменять на полноценную работу с массивами? Type.IsArray, Type.GetElementType()
                if (!_toBytes)
                {
                    return(_data.ToArray(len, attr.Reverse));
                }

                if (ret == null)
                {
                    ret = Array.Empty <byte>();
                }
                using (IEnumerator <byte> en = ((IEnumerable <byte>)ret).GetEnumerator())
                    _result.AddRange(en.ToArray(len, attr.Reverse));
                return(null);

            case CoreType.Byte:
                if (!_toBytes)
                {
                    return(_data.Take(1).Single());
                }

                _result.Add((byte)ret);
                return(null);

            case CoreType.SByte:
                if (!_toBytes)
                {
                    return((sbyte)_data.Take(1).Single());
                }

                _result.Add((byte)(sbyte)ret);
                return(null);

            case CoreType.Char:
                if (len == 0)
                {
                    len = 2;
                }
                if (!_toBytes)
                {
                    return(BitConverter.ToChar(_data.ToArray(len, reverse, 2), 0));
                }

                bytes = BitConverter.GetBytes((char)ret).Take(len);
                _result.AddRange(reverse ? bytes.Reverse() : bytes);
                return(null);

            case CoreType.Float:
                if (len < 4)
                {
                    len = 4;
                }
                if (!_toBytes)
                {
                    return(BitConverter.ToSingle(_data.ToArray(len, reverse, 4), 0));
                }

                bytes = BitConverter.GetBytes((float)ret);
                _result.AddRange(reverse ? bytes.Reverse() : bytes);
                return(null);

            case CoreType.Double:
                if (len < 8)
                {
                    len = 8;
                }
                if (!_toBytes)
                {
                    return(BitConverter.ToDouble(_data.ToArray(len, reverse, 8), 0));
                }

                bytes = BitConverter.GetBytes((double)ret);
                _result.AddRange(reverse ? bytes.Reverse() : bytes);
                return(null);

            case CoreType.Short:
                if (len == 0)
                {
                    len = 2;
                }
                if (!_toBytes)
                {
                    return(BitConverter.ToInt16(_data.ToArray(len, reverse, 2), 0));
                }

                bytes = BitConverter.GetBytes((short)ret).Take(len);
                _result.AddRange(reverse ? bytes.Reverse() : bytes);
                return(null);

            case CoreType.Int:
                if (len == 0)
                {
                    len = 4;
                }
                if (!_toBytes)
                {
                    return(BitConverter.ToInt32(_data.ToArray(len, reverse, 4), 0));
                }

                bytes = BitConverter.GetBytes((int)ret).Take(len);
                _result.AddRange(reverse ? bytes.Reverse() : bytes);
                return(null);

            case CoreType.Long:
                if (len == 0)
                {
                    len = 8;
                }
                if (!_toBytes)
                {
                    return(BitConverter.ToInt64(_data.ToArray(len, reverse, 8), 0));
                }

                bytes = BitConverter.GetBytes((long)ret).Take(len);
                _result.AddRange(reverse ? bytes.Reverse() : bytes);
                return(null);

            case CoreType.UShort:
                if (len == 0)
                {
                    len = 2;
                }
                if (!_toBytes)
                {
                    return(BitConverter.ToUInt16(_data.ToArray(len, reverse, 2), 0));
                }

                bytes = BitConverter.GetBytes((ushort)ret).Take(len);
                _result.AddRange(reverse ? bytes.Reverse() : bytes);
                return(null);

            case CoreType.UInt:
                if (len == 0)
                {
                    len = 4;
                }
                if (!_toBytes)
                {
                    return(BitConverter.ToUInt32(_data.ToArray(len, reverse, 4), 0));
                }

                bytes = BitConverter.GetBytes((uint)ret).Take(len);
                _result.AddRange(reverse ? bytes.Reverse() : bytes);
                return(null);

            case CoreType.ULong:
                if (len == 0)
                {
                    len = 8;
                }
                if (!_toBytes)
                {
                    return(BitConverter.ToUInt64(_data.ToArray(len, reverse, 8), 0));
                }

                bytes = BitConverter.GetBytes((ulong)ret).Take(len);
                _result.AddRange(reverse ? bytes.Reverse() : bytes);
                return(null);

            default:
                return(default(object));
            }
        }