当前位置: 首页 > wzjs >正文

下载手机app客户端下载安装seo在线培训课程

下载手机app客户端下载安装,seo在线培训课程,wordpress框架简介,合肥竞价推广文章目录 前言1 项目结构1.1 整体1.2 代码 2 实现2.1 Processor2.1.1 BaseType2.1.2 CollectionType2.1.3 CustomType 2.2 ByteFormatter2.3 ByteHelper 3 使用 前言 ​ BinaryFormatter 类可以将 C# 类对象快速转换为字节数组数据。 ​ 在网络开发时,不会使用 Bi…

文章目录

  • 前言
  • 1 项目结构
    • 1.1 整体
    • 1.2 代码
  • 2 实现
    • 2.1 Processor
      • 2.1.1 BaseType
      • 2.1.2 CollectionType
      • 2.1.3 CustomType
    • 2.2 ByteFormatter
    • 2.3 ByteHelper
  • 3 使用

前言

​ BinaryFormatter 类可以将 C# 类对象快速转换为字节数组数据。

​ 在网络开发时,不会使用 BinaryFormatter 进行数据序列化和反序列化。因为客户端和服务端的开发语言多数情况下不同,BinaryFormatter 序列化的数据无法兼容其它语言。

​ 因此,需要自定义序列化方式。

​ BinaryFormatter 参考链接:2023-05-27 Unity 2进制4——类对象的序列化与反序列化_unity 二进制序列化-CSDN博客。

  • 项目链接:https://github.com/zheliku/ByteHelper。
  • Unity 版本:6000.0.42f1。

1 项目结构

1.1 整体

image-20250321060632469
  • Scenes(示例场景)
  • Scripts(脚本)
    • ByteHelper(工具脚本)
    • Test(测试脚本)

1.2 代码

image-20250321060653045
  • Processor(存放对应类型的 Processor)
    • BaseType(基本类型的 Processor)
    • CollectionType(集合类型的 Processor)
    • CustomType(自定义类型的 Processor)
    • Processor.cs(抽象基类,用于处理对象和字节数组之间的转换)
  • ByteFormatter.cs(存储所有 Processor,并依据类型进行 Write 与 Read)
  • ByteHelper.cs(封装序列化 API)
  • ReflectionExtension.cs(反射方法拓展)

2 实现

2.1 Processor

​ 一个 Processor 用于处理一种类型的序列化,其包含以下 3 种方法:

  • GetBytesLength

    获取对象在字节数组中占用的字节数。

  • Write

    将对象写入 bytes 数组。

  • Read

    从 bytes 数组中读取对象。

// 抽象类 Processor,用于处理对象和字节数组之间的转换
public abstract class Processor
{public abstract int GetBytesLength(object value);public abstract int Write(byte[] bytes, object value, int index);public abstract int Read(byte[] bytes, int index, out object value);
}

​ 使用泛型版本标识每个 Processor 处理的类型:

public abstract class Processor<TValue> : Processor
{public abstract int GetBytesLength(TValue value);public abstract int Write(byte[] bytes, TValue value, int index);public abstract int Read(byte[] bytes, int index, out TValue value);public override int GetBytesLength(object value){return GetBytesLength((TValue) value);}public override int Write(byte[] bytes, object value, int index){return Write(bytes, (TValue) value, index);}public override int Read(byte[] bytes, int index, out object value){int result = Read(bytes, index, out TValue typedValue);value = typedValue;return result;}
}

2.1.1 BaseType

​ 以 int、string 类型为例:

IntProcessor

  • GetBytesLength

    int 类型使用 4 个子节存储,可直接返回 4,也可使用 sizeof(int)

  • Write

    直接转换为子节,写入 bytes 中的 index 位置。返回值为写入后下一处的位置。

  • Read

    直接将 bytes 中 index 位置的数据读取。返回值为读取后下一处的位置。

public class IntProcessor : Processor<int>
{public override int GetBytesLength(int value){return sizeof(int);}public override int Write(byte[] bytes, int value, int index){BitConverter.GetBytes(value).CopyTo(bytes, index);return index + sizeof(int);}public override int Read(byte[] bytes, int index, out int value){value = BitConverter.ToInt32(bytes, index);return index + sizeof(int);}
}

StringProcessor

​ string 类型长度可变,因此需要先写入长度(int 类型),再写入内容。

  • GetBytesLength:int 长度 + 字符串长度。
  • Write:先写入长度,后写入内容。
  • Read:先读取长度,后读取内容。
public class StringProcessor : Processor<string>
{public override int GetBytesLength(string value){return sizeof(int) + value.Length;}public override int Write(byte[] bytes, string value, int index){BitConverter.GetBytes(value.Length).CopyTo(bytes, index);Encoding.UTF8.GetBytes(value).CopyTo(bytes, index + sizeof(int));return index + sizeof(int) + value.Length;}public override int Read(byte[] bytes, int index, out string value){int length = BitConverter.ToInt32(bytes, index);value = Encoding.UTF8.GetString(bytes, index + sizeof(int), length);return index + sizeof(int) + length;}
}

2.1.2 CollectionType

​ 以 ICollectionProcessor 为例,泛型集合类型需要记录集合本身的 Type 与元素的 Type,因此 ICollectionProcessor 具有 2 个泛型参数。

  • GetBytesLength

    集合长度可变,因此也先写入长度,后顺序写入集合元素。

  • Write

    先写入长度,后顺序写入集合元素。

    使用 ByteFormatter.Write 方法,依据元素类型,自动调用对应的 Processor 写入内容。

  • Read

    先读取长度,后顺序读取集合元素。

    使用 ByteFormatter.Read 方法,依据元素类型,自动调用对应的 Processor 读取内容。

public class ICollectionProcessor<TCollection, TValue> : Processor<TCollection> where TCollection : ICollection<TValue>
{public override int GetBytesLength(TCollection value){var length = sizeof(int);foreach (var item in value){length += ByteFormatter.GetBytesLength(item);}return length;}public override int Write(byte[] bytes, TCollection value, int index){int count = value.Count;// 写长度BitConverter.GetBytes(count).CopyTo(bytes, index);index += sizeof(int); // 留 1 个 int 位置用于写长度// 写内容foreach (var item in value){index = ByteFormatter.Write(bytes, item, index);}return index;}public override int Read(byte[] bytes, int index, out TCollection value){// 1. 读取长度(元素数量)int length = BitConverter.ToInt32(bytes, index);index += sizeof(int);// 2. 读取内容value = (TCollection) Activator.CreateInstance(typeof(TCollection));for (int i = 0; i < length; i++){index = ByteFormatter.Read(bytes, index, typeof(TValue), out var item);value.Add((TValue) item);}return index;}
}

2.1.3 CustomType

​ 自定义类型默认序列化所有的字段,且需要添加 [ByteSerializable] 特性。

  • GetBytesLength

    长度可变,因此也先写入长度,后顺序写入字段。

  • Write

    先写入长度,后使用反射获取所有字段信息,依次写入字段内容。

    反射方法 value.GetFieldValues() 在 ReflectionExtension.cs 中封装,默认获取所有字段的值。

    使用 ByteFormatter.Write 方法,依据字段类型,自动调用对应的 Processor 写入。

  • Read

    先读取长度,后使用反射获取所有字段信息,依次读取字段内容。

    反射方法 obj.GetFieldInfos() 在 ReflectionExtension.cs 中封装,默认获取所有字段的信息。

    使用 ByteFormatter.Read 方法,依据字段类型,自动调用对应的 Processor 读取。

    考虑到值类型,在读取时需要装箱,否则反射赋值时,会将内容赋值给 fieldInfo.SetValue 方法中的临时变量。

public class CustomTypeProcessor<TValue> : Processor<TValue>
{public override int GetBytesLength(TValue value){return sizeof(int) + value.GetFieldValues().Sum(v => ByteFormatter.GetBytesLength(v));}public override int Write(byte[] bytes, TValue value, int index){// 先写长度,以便读取BitConverter.GetBytes(GetBytesLength(value)).CopyTo(bytes, index);index += sizeof(int);var fieldValues = value.GetFieldValues();foreach (var fieldValue in fieldValues){index = ByteFormatter.Write(bytes, fieldValue, index);}return index;}public override int Read(byte[] bytes, int index, out TValue value){var obj = (object) Activator.CreateInstance<TValue>(); // 装箱,以防 TValue 为值类型index += sizeof(int);var fieldInfos = obj.GetFieldInfos();foreach (var fieldInfo in fieldInfos){index = ByteFormatter.Read(bytes, index, fieldInfo.FieldType, out var v);fieldInfo.SetValue(obj, v);}value = (TValue) obj; // 拆箱,还原 valuereturn index;}
}

2.2 ByteFormatter

​ 管理所有类型的 Processor,使用字典存储预置基础类型:

public class ByteFormatter
{// 定义字典,用于存储预置类型的处理器public static readonly Dictionary<Type, object> PRIMITIVE_PROCESSORS = new Dictionary<Type, object>(){{ typeof(int), new IntProcessor() },{ typeof(short), new ShortProcessor() },{ typeof(long), new LongProcessor() },{ typeof(float), new FloatProcessor() },{ typeof(double), new DoubleProcessor() },{ typeof(bool), new BoolProcessor() },{ typeof(char), new CharProcessor() },{ typeof(byte), new ByteProcessor() },{ typeof(string), new StringProcessor() },};...
}

​ 依据传入类型,提供对应的 Processor,若不存在则创建。

  1. 在预置类型的字典中,直接读取
public class ByteFormatter
{// 根据类型获取对应的处理器public static Processor<T> GetProcessor<T>(){return (Processor<T>) GetProcessor(typeof(T));}public static Processor GetProcessor(Type type){// 在预置类型的字典中,直接读取if (PRIMITIVE_PROCESSORS.TryGetValue(type, out object value)) {return (Processor) value;}}
}
  1. 优先处理字典类型,并且先处理 KeyValuePair。

    若字典中存在,则直接读取,否则创建 Processor 添加到字典中再返回。

// 如果类型是 KeyValuePair,则使用 KeyValuePairProcessor
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
{var processorType = typeof(KeyValuePairProcessor<,>).MakeGenericType(type.GetGenericArguments());var processor     = (Processor) Activator.CreateInstance(processorType);PRIMITIVE_PROCESSORS.Add(type, processor); // 添加到字典中return processor;
}// 如果类型是字典,则使用 IDictionaryProcessor
if (type.IsAssignableToGenericInterface(typeof(IDictionary<,>)))
{var processorType = typeof(IDictionaryProcessor<,,>).MakeGenericType(type, type.GetGenericArguments()[0], type.GetGenericArguments()[1]);var processor     = (Processor) Activator.CreateInstance(processorType);PRIMITIVE_PROCESSORS.Add(type, processor); // 添加到字典中return processor;
}
  1. 对于集合类型与用户自定义类型,同理。

    对于用户自定义类型,需要继承 [ByteSerializable] 特性。也可以根据需要自定义其他方式。

// 如果是集合类型,则使用 ICollectionProcessor
if (type.IsAssignableToGenericInterface(typeof(ICollection<>)))
{var processorType = typeof(ICollectionProcessor<,>).MakeGenericType(type, type.GetGenericArguments()[0]);var processor     = (Processor) Activator.CreateInstance(processorType);PRIMITIVE_PROCESSORS.Add(type, processor); // 添加到字典中return processor;
}// 如果拥有 ByteSerializableAttribute 特性,则使用 CustomTypeProcessor
if (type.HasAttribute<ByteSerializableAttribute>())
{var processorType = typeof(CustomTypeProcessor<>).MakeGenericType(type);var processor     = (Processor) Activator.CreateInstance(processorType);PRIMITIVE_PROCESSORS.Add(type, processor); // 添加到字典中return processor;
}

​ 提供 Write 和 Read 方法,依据参数类型,自动获取对应 Processor 处理。

public static int Write<T>(byte[] bytes, T value, int index)
{return GetProcessor<T>().Write(bytes, value, index);
}public static int Write(byte[] bytes, object value, int index)
{return GetProcessor(value.GetType()).Write(bytes, value, index);
}public static int Read<T>(byte[] bytes, int index, out T value)
{return GetProcessor<T>().Read(bytes, index, out value);
}public static int Read(byte[] bytes, int index, Type type, out object value)
{return GetProcessor(type).Read(bytes, index, out value);
}

2.3 ByteHelper

​ 封装序列化方法。

public class ByteHelper
{public const string EXTENSION = ".bytes";public static string BinarySavePath { get; set; } = Application.persistentDataPath + "/Binary";public static byte[] Serialize<TData>(TData data){var processor = ByteFormatter.GetProcessor<TData>();var bytes     = new byte[processor.GetBytesLength(data)];processor.Write(bytes, data, 0);return bytes;}public static TData Deserialize<TData>(byte[] bytes){ByteFormatter.Read<TData>(bytes, 0, out var value);return value;}public static void SaveBytes<TData>(string filePath, TData data, string extension = EXTENSION){string fullPath = Path.Combine(BinarySavePath, filePath);fullPath = Path.ChangeExtension(fullPath, EXTENSION);var directory = Path.GetDirectoryName(fullPath);if (directory != null && !Directory.Exists(directory)){Directory.CreateDirectory(directory);}byte[] bytes = Serialize(data);File.WriteAllBytes(fullPath, bytes);#if UNITY_EDITORUnityEditor.AssetDatabase.Refresh();
#endif}public static TData LoadBytes<TData>(string filePath, string extension = ByteHelper.EXTENSION){string fullPath = Path.Combine(BinarySavePath, filePath);fullPath = Path.ChangeExtension(fullPath, EXTENSION);if (!File.Exists(fullPath)){ // 不存在文件,则警告,并返回默认值Debug.LogWarning($"ByteHelper: Can't find path \"{fullPath}\"");return default(TData);}byte[] bytes = File.ReadAllBytes(fullPath);return Deserialize<TData>(bytes);}
}

3 使用

​ 以 CustomType 为例,打开 CustomType 场景,点击 “TestScript” 物体,右侧编辑你想要的数据。

image-20250321064638712

​ 运行场景,会自动序列化数据,对应脚本如下:

[Serializable] [ByteSerializable]
public class CustomData
{public  int        Id;private string     _name;public  List<int>  List = new List<int>();public  NestedData NestedData; // Supports nested typespublic string Name{get => _name;set => _name = value;}public override string ToString() {...}
}[Serializable] [ByteSerializable]
public struct NestedData
{public bool Bool;public override string ToString() {...}
}public class Test_CustomType : MonoBehaviour
{public Button Btn;public Text Text;[Header("Set Your CustomData")]public CustomData CustomData;public NestedData NestedData;private void Start(){CustomData.Name = "zheliku"; // 设置私有字段SerializeData();Text.text = "Serialize Data Success!";Btn.onClick.AddListener(OnClick);}// 序列化数据,保存到 CustomType 目录下。private void SerializeData(){ByteHelper.SaveBytes($"CustomType/{nameof(CustomData)}", CustomData);ByteHelper.SaveBytes($"CustomType/{nameof(NestedData)}", NestedData);}// 点击按钮时,显示反序列化数据public void OnClick(){Text.text = "CustomData: " + ByteHelper.LoadBytes<CustomData>($"CustomType/{nameof(CustomData)}") + "\n\n" +"NestedData: " + ByteHelper.LoadBytes<NestedData>($"CustomType/{nameof(NestedData)}");}
}

​ 点击按钮,即可显示反序列化数据:

image-20250321065016604

​ 点击 “ByteHelper/Open Binary Folder” 可打开序列化数据存储目录。

image-20250321065044534

​ CustomType 保存在 “CustomType” 目录下。

image-20250321065216116

​ CustomData 的序列化内容如下:

image-20250321065254361

​ 根据需要可自行扩展 Processor。

http://www.dtcms.com/wzjs/98986.html

相关文章:

  • 长春集团网站建设培训学校
  • 惠州seo排名公司吉林seo刷关键词排名优化
  • 南京做网站建设的公司哪家好网络热词2023
  • 深圳外贸网站搭建南宁百度关键词排名公司
  • 软件网站是怎么做的2023年免费b站推广大全
  • 襄樊seo网站seo设计
  • 求个网站2021seo建站优化推广
  • 做网站从何开始武汉seo 网络推广
  • 玉环建设局网站公司建网站需要多少钱
  • 食品网站建设网站定制开发百度推广四川成都地区服务中心
  • 国外psd网页模板网站郑州seo地址
  • 做暧暖ox免费网站线上推广有哪些
  • 企业网站不被百度收录企业营销策划书如何编写
  • 能免费建手机网站吗百度收录提交网站后多久收录
  • wordpress付费破解版首页排名seo
  • 武汉疫情站长工具的使用seo综合查询运营
  • 资源网站很难做谷歌浏览器手机版免费官方下载
  • 网站建设标准流程及外包注意事项建站公司最新报价
  • 沧州做网站aso优化方案
  • 一个虚拟主机可以放几个网站广州番禺发布
  • 浪尖工业设计公司官网短视频搜索优化
  • 做传单的网站宁波网站推广优化
  • 天水市秦州区作风建设年网站网络广告公司排名
  • 网站建设彩票网优化快速排名公司
  • 博彩网站做代理赚钱吗seo网站优化工具大全
  • it美工做网站接外贸订单的渠道平台哪个好
  • 番禺做网站平台公众号怎么开通
  • 建设网站要买服务器新华传媒b2b商务平台
  • 模具做外贸网站重庆网站建设与制作
  • 广州建站模板厂家如何网络媒体推广