自学内容网 自学内容网

c#自动编译序列化反序列化

实现目的

在一个游戏项目中可能需要有大量c#类型需要序列化和反序列化(如配置表,游戏运行数据)需要保存或从文本中读取。为了减少一个个去编辑序列化和反序列化的工作量,需要有一个简单的方法对这些c#类型进行处理,一键生成代码。

实现效果

该处声明了一个类,实现了一个接口,并将需要序列化和反序列化的字段用自定义特性标签(SerializeAttribute)封装,再通过反射拿到该类的标签,根据其类型依次写入序列化和反序列化方法。

public class ConfigDataBase : ISerializeable {
// 此处为注册的需要序列化标签
    [SerializeAttribute("ID")]
    public int ID;

    public void AfterDeSerialize()
    {
    }

    public void BeforeSerialize()
    {
    }
    //以下为代码生成部分
    #region AutoGenerate    
    public virtual void Serialize(System.IO.BinaryWriter writer)
    {
        writer.Write(ID);
    }
        
    public virtual void Deserialize(System.IO.BinaryReader reader)
    {
        ID =  reader.ReadInt32();
    }
    #endregion
}

实现

自定义序列化接口

public interface ISerializeable 
{
    /// <summary>
    /// 在序列化之前调用,只能用于将自己节点引用型转换为数据
    /// </summary>
    public void BeforeSerialize();

    /// <summary>
    /// 序列化
    /// </summary>
    /// <param name="writer">二进制写入器</param>
    public void Serialize(BinaryWriter writer);


    /// <summary>
    /// 在反序列化之后调用,只能写序列化后自己节点处理相关引用型参数
    /// </summary>
    public void AfterDeSerialize();
    /// <summary>
    /// 反序列化
    /// </summary>
    /// <param name="reader">二进制读取器</param>
    public void Deserialize(BinaryReader reader);
}

自定义特性标签

[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Enum)]
public class SerializeAttribute : Attribute
{
    public readonly string name;
    public SerializeAttribute(string name) {
        this.name = name;
    }
}

BinaryWriter,BinaryReader扩展

这里按自己项目需要的类型来,想要支持啥类型就写啥,不需要就不写。

public static class BinaryReaderExpand 
{

    public static T ReadSerializeable<T>(this BinaryReader reader) where T: ISerializeable,new()
    {
        T t = new T();
        bool isHasValue =  reader.ReadBoolean();
        if (isHasValue) {
            t.Deserialize(reader);
            t.AfterDeSerialize();
        }
        return t;
    }
    public static int[] ReadIntArr(this BinaryReader reader)
    {
        int length = reader.ReadInt32();
         int[] arr = new int[length];
        if (length == 0)
        {
            return arr;
        }
        for (int i = 0; i < length; i++)
        {
            arr[i] = reader.ReadInt32();
        }
        return arr;
    }
    public static string[] ReadStringArr(this BinaryReader reader)
    {
        int length = reader.ReadInt32();
        string[] arr = new string[length];
        if (length == 0)
        {
            return arr;
        }
        for (int i = 0; i < length; i++)
        {
            arr[i] = reader.ReadString();
        }
        return arr;
    }
    public static double[] ReadDoubleArr(this BinaryReader reader)
    {
        int length = reader.ReadInt32();
        double[] arr = new double[length];
        if (length == 0)
        {
            return arr;
        }
        for (int i = 0; i < length; i++)
        {
            arr[i] = reader.ReadDouble();
        }
        return arr;
    }

    public static bool[] ReadBooleanArr(this BinaryReader reader)
    {
        int length = reader.ReadInt32();
        bool[] arr = new bool[length];
        if (length == 0)
        {
            return arr;
        }
        for (int i = 0; i < length; i++)
        {
            arr[i] = reader.ReadBoolean();
        }
        return arr;
    }

    public static float[] ReadFloatArr(this BinaryReader reader)
    {
        int length = reader.ReadInt32();
        float[] arr = new float[length];
        if (length == 0)
        {
            return arr;
        }
        for (int i = 0; i < length; i++)
        {
            arr[i] = reader.ReadSingle();
        }
        return arr;
    }

    public static long[] ReadLongArr(this BinaryReader reader)
    {
        int length = reader.ReadInt32();
        long[] arr = new long[length];
        if (length == 0)
        {
            return arr;
        }
        for (int i = 0; i < length; i++)
        {
            arr[i] = reader.ReadInt64();
        }
        return arr;
    }

    public static List<int> ReadIntList(this BinaryReader reader)
    {
        int length = reader.ReadInt32();
        List<int> arr = new List<int>(length);
        if (length == 0)
        {
            return arr;
        }
        for (int i = 0; i < length; i++)
        {
            arr[i] = reader.ReadInt32();
        }
        return arr;
    }

    public static List<T> ReadSerializeableList<T>(this BinaryReader reader)  where T: ISerializeable,new()
    {
        int length = reader.ReadInt32();
        List<T> arr = new List<T>(length);
        if (length == 0)
        {
            return arr;
        }
        for (int i = 0; i < length; i++)
        {
            arr[i] = reader.ReadSerializeable<T>();
        }
        return arr;
    }
}

public static class BinaryWriterExpand 
{
    
    public static void WriteSerializeable(this BinaryWriter writer, ISerializeable sa)
    {

        writer.Write(sa != null);
        if (sa == null) return;
        sa.BeforeSerialize();
        sa.Serialize(writer);
    }
    public static void Write(this BinaryWriter writer, int[] arr)
    {

        writer.Write(arr.Length);
        if (arr.Length ==0) {
            return;
        }
        foreach (var item in arr)
        {
            writer.Write(item);
        }
    } 
    public static void Write(this BinaryWriter writer, string[] arr)
    {
        writer.Write(arr.Length);
        if (arr.Length ==0) {
            return;
        }
        foreach (var item in arr)
        {
            writer.Write(item);
        }
    }
    public static void Write(this BinaryWriter writer, double[] arr)
    {
        writer.Write(arr.Length);
        if (arr.Length ==0) {
            return;
        }
        foreach (var item in arr)
        {
            writer.Write(item);
        }
    } 
    
    public static void Write(this BinaryWriter writer, bool[] arr)
    {
        writer.Write(arr.Length);
        if (arr.Length ==0) {
            return;
        }
        foreach (var item in arr)
        {
            writer.Write(item);
        }
    }

    public static void Write(this BinaryWriter writer, float[] arr)
    {
        writer.Write(arr.Length);
        if (arr.Length == 0)
        {
            return;
        }
        foreach (var item in arr)
        {
            writer.Write(item);
        }
    } 
    
    public static void Write(this BinaryWriter writer, long[] arr)
    {
        writer.Write(arr.Length);
        if (arr.Length == 0)
        {
            return;
        }
        foreach (var item in arr)
        {
            writer.Write(item);
        }
    }

    public static void Write(this BinaryWriter writer, List<int> arr)
    {
        writer.Write(arr.Count);
        if (arr.Count == 0)
        {
            return;
        }
        foreach (var item in arr)
        {
            writer.Write(item);
        }
    }

    public static void Write(this BinaryWriter writer, List<ISerializeable> arr)
    {
        writer.Write(arr.Count);
        if (arr.Count == 0)
        {
            return;
        }
        foreach (var item in arr)
        {
            writer.WriteSerializeable(item);
        }
    }
}

注册需要编译的代码列表

简单来说就是声明编译的类的类型和文件路径。方便编译。

public static class GenerateRegister
{
    public static List<GenerateInfo> list = new List<GenerateInfo>();
    public static List<GenerateInfo> Glist = new List<GenerateInfo>();

    static GenerateRegister()
    {
        GameConfigRegister();
        Register();
    }
    private static void GameConfigRegister()
    {
        


    }
    private static void Register()
    {
        //model

        AddAutoGenerate<PlayerModel>("PlayerModel", "Scripts/Model");
        AddAutoGenerate<LevelModel>("LevelModel", "Scripts/Model");
        AddAutoGenerate<MoneyModel>("MoneyModel", "Scripts/Model");
        AddAutoGenerate<GameController>("GameController", "Scripts");


        AddAutoGenerate<HeroData>("HeroData", "Scripts/Hero");
        AddAutoGenerate<MonitorGroup>("MonitorGroup", "Scripts/MonitorListener");
        AddAutoGenerate<HeroTransfeData>("HeroTransfeData", "Scripts/Hero");
        AddAutoGenerate<ConfigDataBase>("ConfigBase", "Scripts/config");
        AddAutoGenerate<ConfigDataBase>("ConfigBase", "Scripts/config");

    }

    public static void AddAutoGenerate<T>(string name,string cspath) {
        GenerateInfo info = new GenerateInfo(typeof(T), name, cspath);
        Glist.Add(info);
    }

    private static void AddSaveInfo<T>(string name, string path) {
        GenerateInfo info = new GenerateInfo(typeof(T),name,path);
        list.Add(info);
    }


}

public class GenerateInfo
{
    public Type t;
    public string SavePath;
    public string name;
    private List<MemberInfo> sFullProps;

    public GenerateInfo(Type t,string name, string path){
        this.t = t;
        this.name = name;
        this.SavePath = path;
    }

    public List<MemberInfo> GetFullProps()
    {
    // 第一次调用生成一次以后就不需要了
        sFullProps = new List<MemberInfo>();
        PropertyInfo[] pinfo = t.GetProperties();
        FieldInfo[] finfo = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic| BindingFlags.Instance);

        foreach (var item in pinfo)
        {
            if (item.GetCustomAttribute<SerializeAttribute>() != null)
            {
                sFullProps.Add(item);
            }
        }
        foreach (var item in finfo)
        {
            if (item.GetCustomAttribute<SerializeAttribute>() != null)
            {
                sFullProps.Add(item);
            }
        }
    return sFullProps;
    }
}

编译(核心代码)

因为这个部分我使用的是unity引擎,所以采用的是unity扩展的方式。
简单来说就是读取之前的声明,并找到 #region AutoGenerate 和 #endregion
的位置,并根据c#类型,反射拿取特性标签的内容,拿到字段类型,再往文件插入相应的BinaryWriter,BinaryReader扩展方法。


public class AutoGenerate : Editor
{
    private static string programPath = Application.dataPath;

    [MenuItem("Pack/编译cs")]
    static void GenerateCS()
    {
        foreach (var item in GenerateRegister.Glist)
        {
            GenerateOneCs(item);
        }
    }

    private static void GenerateOneCs(GenerateInfo info) {

        string fullPath = programPath+"/" + info.SavePath +"/" +info.name +".cs";

        if (!File.Exists(fullPath)) {
            Debug.LogErrorFormat("未找到cs文件:{0}",fullPath);
            return;
        }
        string text;
        using (StreamReader sr = new StreamReader(fullPath, System.Text.Encoding.UTF8))
        {
            text = sr.ReadToEnd(); 
        }
        text.Replace("\r\r\n","\r\n");
        int startindex = text.IndexOf("#region AutoGenerate");
        int endindex = text.IndexOf("#endregion", startindex);

        if (startindex<=0 || endindex <=0) {
            Debug.LogError("未找到#region AutoGenerate或#endregion :"+info.name);
            return;
        }

        string beforeText = text.Substring(0,startindex+20);
        string afterText = text.Substring(endindex);

        int pos = startindex;
        int spaceC = 0;
        while (pos > 0) {
            char c = text[pos];
            if (c == '\n') break;
            if (c == ' ') {
                spaceC += 1;
            }
            if (c == '\t')
            {
                spaceC += 4;
            }
            pos--;
        }
        // 
        int indent = Mathf.FloorToInt(spaceC / 4f);
        StringBuilder stringBuilder = new StringBuilder();

        writeSerializeFunc(indent,info.t,info.GetFullProps(),stringBuilder);
        writeDerializeFunc(indent,info.t,info.GetFullProps(),stringBuilder);


        string endtext = beforeText + stringBuilder.ToString() + afterText;
        using (StreamWriter swriteWriter = new StreamWriter(fullPath,false, System.Text.Encoding.UTF8))
        {
            swriteWriter.Write(endtext);  // 向文件写入文本内容
        }
    }

    private static void writeSerializeFunc(int indent,Type t, List<MemberInfo> members, StringBuilder builder) {
        if (t.BaseType == null || !(typeof(ISerializeable).IsAssignableFrom(t.BaseType)))
        {
            builder.WriteOneLine(indent,"");
            builder.WriteOneLine(indent, "public virtual void Serialize(System.IO.BinaryWriter writer)");
        }
        else {
            builder.WriteOneLine(indent, "");

            builder.WriteOneLine(indent, "public override void Serialize(System.IO.BinaryWriter writer)");
        }
        builder.WriteOneLine(indent, "{");
        indent++;
        foreach (var item in members)
        {
            GenerateSerialize(indent, item, builder);
        }
        indent--;
        builder.WriteOneLine(indent, "}");
        builder.WriteIndent(indent);


    }

    /// <summary>
    /// 先这样后面有新类型再加
    /// </summary>
    /// <param name="indent"></param>
    /// <param name="member"></param>
    /// <param name="sb"></param>
    public static void GenerateSerialize(int indent, MemberInfo member,StringBuilder sb) {
        Type propertyType = member is FieldInfo ? (member as FieldInfo).FieldType : (member as PropertyInfo).PropertyType;
        if (propertyType.IsEnum)
        {
            switch (GetEnumBaseType(propertyType))
            {
                case TypeCode.Int32:
                    sb.WriteOneLineFormat(indent, "writer.Write((int){0});", member.Name);
                    break;
                case TypeCode.String:
                    sb.WriteOneLineFormat(indent, "writer.Write((String){0});", member.Name);
                    break;
                default:
                    break;
            }
            return;
        }
        else if (propertyType.IsArray || (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>)))
        {
            sb.WriteOneLineFormat(indent, "writer.Write({0});", member.Name);
        }
        else if (propertyType.IsClass)
        {
            if (propertyType == typeof(string))
            {
                sb.WriteOneLineFormat(indent, "writer.Write({0});", member.Name);
            }
            else if (typeof(ISerializeable).IsAssignableFrom(propertyType))
            {
                sb.WriteOneLineFormat(indent, "writer.WriteSerializeable((ISerializeable){0});", member.Name);
            }
            else
            {
                Debug.LogError("未知的类型:" + propertyType.Name);
            }
        }
        else {
            sb.WriteOneLineFormat(indent, "writer.Write({0});", member.Name);
        }
    }

    private static void writeDerializeFunc(int indent,Type t, List<MemberInfo> members, StringBuilder builder) {
        if (t.BaseType == null || !(typeof(ISerializeable).IsAssignableFrom(t.BaseType )))
        {
            builder.WriteOneLine(indent,"");
            builder.WriteOneLine(indent, "public virtual void Deserialize(System.IO.BinaryReader reader)");
        }
        else {
            builder.WriteOneLine(indent, "");
            builder.WriteOneLine(indent, "public override void Deserialize(System.IO.BinaryReader reader)");
        }
        builder.WriteOneLine(indent, "{");
        indent++;
        foreach (var item in members)
        {
            GenerateDeserialize(indent, item, builder);
        }
        indent--;
        builder.WriteOneLine(indent, "}");
        builder.WriteIndent(indent);


    }

    /// <summary>
    /// </summary>
    /// <param name="indent"></param>
    /// <param name="member"></param>
    /// <param name="sb"></param>
    public static void GenerateDeserialize(int indent, MemberInfo member,StringBuilder sb) {
        Type propertyType = member is FieldInfo ? (member as FieldInfo).FieldType : (member as PropertyInfo).PropertyType;
        if (propertyType.IsEnum)
        {
            switch (GetEnumBaseType(propertyType))
            {
                case TypeCode.Int32:
                    sb.WriteOneLineFormat(indent, "this.{0} =({1})reader.ReadInt32();", member.Name, propertyType.Name);
                    break;
                case TypeCode.String:
                    sb.WriteOneLineFormat(indent, "this.{0} = ({1})reader.ReadString();", member.Name, propertyType.Name);
                    break;
                default:
                    break;
            }
            return;
        }
        else if (propertyType.IsArray || (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>)))
        {
            if (propertyType == typeof(int[]))
            {
                sb.WriteOneLineFormat(indent, "this.{0} = reader.ReadIntArr();", member.Name);
            }
            else if (propertyType == typeof(string[]))
            {
                sb.WriteOneLineFormat(indent, "this.{0} = reader.ReadStringArr();", member.Name);
            }
            else if (propertyType == typeof(double[]))
            {
                sb.WriteOneLineFormat(indent, "this.{0} = reader.ReadDoubleArr();", member.Name);
            }
            else if (propertyType == typeof(bool[]))
            {
                sb.WriteOneLineFormat(indent, "this.{0} = reader.ReadBooleanArr();", member.Name);
            }
            else if (propertyType == typeof(float[]))
            {
                sb.WriteOneLineFormat(indent, "this.{0} = reader.ReadFloatArr();", member.Name);
            }
            else if (propertyType == typeof(long[]))
            {
                sb.WriteOneLineFormat(indent, "this.{0} = reader.ReadLongArr();", member.Name);
            }
            else if (propertyType == typeof(List<int>))
            {
                sb.WriteOneLineFormat(indent, "this.{0} = reader.ReadIntList();", member.Name);
            }
            else if (typeof(ISerializeable).IsAssignableFrom(propertyType.GetGenericArguments()[0]))
            {
                sb.WriteOneLineFormat(indent, "this.{0} = reader.ReadSerializeableList<T>();", member.Name, propertyType.Name);
            }

        }
        else if (propertyType.IsClass)
        {
            if (propertyType == typeof(string))
            {
                sb.WriteOneLineFormat(indent, "{0} = reader.ReadString();", member.Name);
            }
            else if (typeof(ISerializeable).IsAssignableFrom(propertyType))
            {
                sb.WriteOneLineFormat(indent, "{0} = reader.ReadSerializeable<{1}>();", member.Name, propertyType.Name);
            }
            else
            {
                Debug.LogError("未知的类型:" + propertyType.Name);
            }
        }
        else if (propertyType.Name == "Int32")
        {
            sb.WriteOneLineFormat(indent, "{0} =  reader.ReadInt32();", member.Name);
        }
        else {
            sb.WriteOneLineFormat(indent, "err", member.Name);
        }
    }





    private static TypeCode GetEnumBaseType(Type type)
    {
        Type underlyingType = Enum.GetUnderlyingType(type);

        if (underlyingType == typeof(int))
        {
            return TypeCode.Int32;
        }
        if (underlyingType == typeof(sbyte))
        {
            return TypeCode.SByte;
        }
        if (underlyingType == typeof(short))
        {
            return TypeCode.Int16;
        }
        if (underlyingType == typeof(long))
        {
            return TypeCode.Int64;
        }
        if (underlyingType == typeof(uint))
        {
            return TypeCode.UInt32;
        }
        if (underlyingType == typeof(byte))
        {
            return TypeCode.Byte;
        }
        if (underlyingType == typeof(ushort))
        {
            return TypeCode.UInt16;
        }
        if (underlyingType == typeof(ulong))
        {
            return TypeCode.UInt64;
        }
        if (underlyingType == typeof(bool))
        {
            return TypeCode.Boolean;
        }

        if (underlyingType != typeof(char))
        {
            throw new InvalidOperationException("InvalidOperation_UnknownEnumType");
        }

        return TypeCode.Char;

    }
}


原文地址:https://blog.csdn.net/qq_47503001/article/details/142709535

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!