NETool.Generator 4.3.1

dotnet add package NETool.Generator --version 4.3.1
                    
NuGet\Install-Package NETool.Generator -Version 4.3.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NETool.Generator" Version="4.3.1">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NETool.Generator" Version="4.3.1" />
                    
Directory.Packages.props
<PackageReference Include="NETool.Generator">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NETool.Generator --version 4.3.1
                    
#r "nuget: NETool.Generator, 4.3.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NETool.Generator@4.3.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NETool.Generator&version=4.3.1
                    
Install as a Cake Addin
#tool nuget:?package=NETool.Generator&version=4.3.1
                    
Install as a Cake Tool

SunnyUI.Net

NETool.Generator

介绍

NETool.Generator 是 NETool 的适用于 C# 的零编码二进制序列化程序。
参考 MemoryPack,支持的类型不及其丰富,但足以满足大部分需求。
NETool.Pack 速度之所以如此之快,是由于其特定于 C# 优化的二进制格式,利用 .NET 8+ 和 C# 13 以及增量源生成器 IIncrementalGenerator(.NET Standard 2.0),另外包括 Span, ReadOnlySpan 以及内存池 ArrayPool 的使用。

除了二进制序列化,基于源生成还实现了与 Windows API 无关的 Ini 格式的配置文件读写, 方便用户可通过文本工具进行配置的修改。

软件架构

.NETFramework 4.7.2, .NET8, .NET10

安装教程

Nuget 安装最新版本。

Install-Package NETool.Generator

快速入门

定义要二进制序列化的类,并使用属性和关键字对其进行注释。[NEToolPackable] partial

定义要 Ini 格式序列化的类,并使用属性和关键字对其进行注释。[IniConfig(string fileName)] partial

using System.NETool;

[NEToolPackable]
[IniConfig("Person.ini")]
public partial class Person
{
    public int Age { get; set; }
    public string Name { get; set; }

    [NEToolIgnore]
    public string Address { get; set; }
}

注:NEToolPackable 与 IniConfig 特性可以单独按需要使用。

源生成的代码实现了 INEToolPackMembers 接口:

/// <summary>
/// NETool 序列化成员说明接口
/// </summary>
public interface INEToolPackMembers
{
    /// <summary>
    /// 排除数据字段
    /// </summary>
    string ExcludedMembers();

    /// <summary>
    /// 未知数据字段
    /// </summary>
    string UnknownMembers();
}

源生成的代码实现了 INEToolPackable 接口,用于二进制序列化:

/// <summary>
/// NETool 序列化接口
/// </summary>
public interface INEToolPackable
{
    /// <summary>
    /// NETool 序列化方法
    /// </summary>
    /// <param name="endian">字节序: Little 为小端字节(低字节序),Big 为大端字节序(高字节序)</param>
    /// <param name="encoding">字符编码</param>
    /// <param name="zipType">压缩及加密算法类型</param>
    /// <param name="password">密码</param>
    /// <returns>字节数组对象</returns>
    ByteArray Serialize(Endian endian = Endian.Little, StringEncoding encoding = StringEncoding.UTF8, ZipType zipType = ZipType.None, string password = null);

    /// <summary>
    /// NETool 反序列化方法
    /// </summary>
    /// <param name="span">数据</param>
    /// <param name="endian">字节序: Little 为小端字节(低字节序),Big 为大端字节序(高字节序)</param>
    /// <param name="encoding">字符编码</param>
    /// <param name="zipType">压缩及加密算法类型</param>
    /// <param name="password">密码</param>
    void Deserialize(scoped ReadOnlySpan<byte> span, Endian endian = Endian.Little, StringEncoding encoding = StringEncoding.UTF8, ZipType zipType = ZipType.None, string password = null);
}

另外还实现了两个深度拷贝函数:

/// <summary>
/// 获取已有 Sunny.Serialization.Demo.Person 对象的副本,需有默认的无参构造函数
/// </summary>
/// <returns>Sunny.Serialization.Demo.Person 副本</returns>
public Sunny.Serialization.Demo.Person DeepCopy()

    /// <summary>
/// 将已有 Sunny.Serialization.Demo.Person 对象深度拷贝到另一个不为空的 Sunny.Serialization.Demo.Person 对象
/// </summary>
/// <param name="other">另一个 Sunny.Serialization.Demo.Person 对象</param>
public void DeepCopyTo(Sunny.Serialization.Demo.Person other)

源生成的代码实现了 IIniConfig 接口,用于 Ini 格式配置文件加载和保存:

/// <summary>
/// 定义一个接口,用于 Ini 格式配置文件加载和保存
/// </summary>
public interface IIniConfig
{
    /// <summary>
    /// 从文件加载配置
    /// </summary>
    void LoadIni();

    /// <summary>
    /// 保存配置到文件
    /// </summary>
    void SaveIni();

    /// <summary>
    /// 异步从文件加载配置
    /// </summary>
    Task LoadIniAsync();

    /// <summary>
    /// 异步保存配置到文件
    /// </summary>
    Task SaveIniAsync();

    /// <summary>
    /// 从文件加载配置
    /// </summary>
    /// <param name="fileName">文件名</param>
    void LoadIni(string fileName);

    /// <summary>
    /// 保存配置到文件
    /// </summary>
    /// <param name="fileName">文件名</param>
    void SaveIni(string fileName);

    /// <summary>
    /// 异步从文件加载配置
    /// </summary>
    /// <param name="fileName">文件名</param>
    Task LoadIniAsync(string fileName);

    /// <summary>
    /// 异步保存配置到文件
    /// </summary>
    /// <param name="fileName">文件名</param>
    Task SaveIniAsync(string fileName);
}

序列化代码由实现接口的 C# 源生成器功能生成。

  • 在 Visual Studio 中,可以使用类名称上的快捷方式 F12 检查生成的代码,然后选择 *.Person.Pack.Properties.g.cs,此为二进制序列化生成代码:
// <auto-generated by NETool.Generator 2025-08-21 14:14:31/>
// ReSharper disable once InconsistentNaming

using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.NETool;
using System.Runtime.CompilerServices;
using System.Text;

namespace Sunny.Serialization.Demo;

/// <summary>
/// Sunny.Serialization.Demo.Person 显示排除与未知的成员
/// </summary>
public partial class Person : INEToolPackMembers
{
    /// <summary>
    /// 排除的成员 1 个
    /// </summary>
    public string ExcludedMembers()
    {
        var sb = new ValueStringBuilder();
        sb.AppendLine("忽略类型: string Address [标记 Ignore 特性;];");
        return sb.ToString();
    }

    /// <summary>
    /// 未知的成员 0 个
    /// </summary>
    public string UnknownMembers()
    {
        return string.Empty;
    }
}

/// <summary>
/// Sunny.Serialization.Demo.Person 二进制序列化扩展
/// </summary>
public partial class Person : INEToolPackable
{
    ///<inheritdoc/>
    public ByteArray Serialize(Endian endian = Endian.Little, StringEncoding encoding = StringEncoding.UTF8, ZipType zipType = ZipType.None, string password = null)
    {
        var writer = new ByteArray(endian, encoding);
        #region 序列化
        // int Age
        writer.WriteInt32(this.Age);
        // string Name
        writer.WriteString(this.Name);
        #endregion 序列化
        var result = zipType.Compress(writer.Span, password);
        writer.Dispose();
        return result;
    }

    ///<inheritdoc/>
    public void Deserialize(scoped ReadOnlySpan<byte> span, Endian endian = Endian.Little, StringEncoding encoding = StringEncoding.UTF8, ZipType zipType = ZipType.None, string password = null)
    {
        var unZipped = zipType.Decompress(span, password);
        var reader = new ByteArray(unZipped.Span, endian, encoding);
        #region 反序列化
        // int Age
        this.Age = reader.ReadInt32();
        // string Name
        this.Name = reader.ReadString();
        #endregion 反序列化
        unZipped.Dispose();
    }

    /// <summary>
    /// 获取已有 Sunny.Serialization.Demo.Person 对象的副本,需有默认的无参构造函数
    /// </summary>
    /// <returns>Sunny.Serialization.Demo.Person 副本</returns>
    public Sunny.Serialization.Demo.Person DeepCopy()
    {
        using var writer = this.Serialize();
        var copy = new Sunny.Serialization.Demo.Person();
        copy.Deserialize(writer.Span);
        return copy;
    }

    /// <summary>
    /// 将已有 Sunny.Serialization.Demo.Person 对象深度拷贝到另一个不为空的 Sunny.Serialization.Demo.Person 对象
    /// </summary>
    /// <param name="other">另一个 Sunny.Serialization.Demo.Person 对象</param>
    public void DeepCopyTo(Sunny.Serialization.Demo.Person other)
    {
        if (other is null) return;
        
        using var writer = this.Serialize();
        other.Deserialize(writer.Span);
    }

}

调用序列化/反序列化对象实例:

 var person = new Person
 {
     Age = 30,
     Name = "Sunny"
 };

 // 序列化
 var byteArray = person.Serialize();
 Console.WriteLine($"Serialized Person: {byteArray.Span.ToHexString()}");

 // 反序列化
 person.Deserialize(byteArray.Span);
 Console.WriteLine($"Deserialized Person: Age={person.Age}, Name={person.Name}");
 byteArray.Dispose();

 // 深拷贝
 Person other = person.DeepCopy();
 Console.WriteLine($"Deep Copied Person: Age={other.Age}, Name={other.Name}");

输出结果:

Serialized Person: 1E0000000553756E6E79
Deserialized Person: Age=30, Name=Sunny
Deep Copied Person: Age=30, Name=Sunny
  • 在 Visual Studio 中,可以使用类名称上的快捷方式 F12 检查生成的代码,然后选择 *.Person.Ini.Properties.g.cs,此为 Ini 格式配置文件生成代码:
// <auto-generated by NETool.Generator 2025-10-18 10:53:34/>
// ReSharper disable once InconsistentNaming

using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.NETool;
using System.Runtime.CompilerServices;
using System.Text;

namespace Sunny.Serialization.Demo;

/// <summary>
/// Sunny.Serialization.Demo.Person Ini 配置文件序列化扩展
/// </summary>
public partial class Person : IIniConfig
{
    /// <summary>
    /// Sunny.Serialization.Demo.Person 单例对象
    /// </summary>
    [NEToolIgnore]
    public static Sunny.Serialization.Demo.Person Instance = Singleton<Sunny.Serialization.Demo.Person>.Instance;

    /// <summary>
    /// Ini 配置文件名称
    /// </summary>
    [NEToolIgnore]
    private const string __IniFileName = "Person.ini";

    /// <summary>
    /// Ini 配置文件全路径
    /// </summary>
    [NEToolIgnore]
    public string IniFileName => FileEx.VerifyFileName(__IniFileName);

    ///<inheritdoc/>
    public void InitIni(){}

    ///<inheritdoc/>
    public void SaveIni(string fileName = __IniFileName)
    {
        var writer = new IniFile(fileName, Encoding.UTF8, false);
        __IniWriting(writer);
        writer.SaveAs(fileName);
    }

    ///<inheritdoc/>
    public Task SaveIniAsync(string fileName = __IniFileName)
    {
        var writer = new IniFile(fileName, Encoding.UTF8, false);
        __IniWriting(writer);
        return writer.SaveAsAsync(fileName);
    }

    ///<inheritdoc/>
    public void LoadIni(string fileName = __IniFileName)
    {
        if (fileName == __IniFileName && !File.Exists(IniFileName))
        {
            InitIni();
            SaveIni();
        }

        var reader = new IniFile(fileName, Encoding.UTF8, false);
        reader.Load();
        __IniReading(reader);
    }

    ///<inheritdoc/>
    public async Task LoadIniAsync(string fileName = __IniFileName)
    {
        if (fileName == __IniFileName && !File.Exists(IniFileName))
        {
            InitIni();
            await SaveIniAsync().ConfigureAwait(false);
        }

        var reader = new IniFile(fileName, Encoding.UTF8, false);
        await reader.LoadAsync().ConfigureAwait(false);
        __IniReading(reader);
    }

    /// <summary>
    /// 写入数据
    /// </summary>
    /// <param name="writer">IniFile 写入器</param>
    private IniFile __IniWriting(IniFile writer)
    {
        #region 序列化
        const string __header = $"count";
        var section = "Person";
        // int Age
        writer.WriteInt32(section, "Age", this.Age, "年龄");
        // string Name
        writer.WriteString(section, "Name", this.Name, "姓名");
        #endregion 序列化
        return writer;
    }

    /// <summary>
    /// 读取数据
    /// </summary>
    /// <param name="reader">IniFile 读取器</param>
    private void __IniReading(IniFile reader)
    {
        #region 反序列化
        const string __header = $"count";
        var section = "Person";
        // int Age
        this.Age = reader.ReadInt32(section, "Age", this.Age);
        // string Name
        this.Name = reader.ReadString(section, "Name", this.Name);
        #endregion 反序列化
    }

}

调用 Ini 读写序列化/反序列化对象实例:

var person = new Person
{
    Age = 30,
    Name = "Sunny"
};

await person.SaveIniAsync();
var other2 = new Person();
await other2.LoadIniAsync().ConfigureAwait(false);
Console.WriteLine($"Ini load Person: Age={other2.Age}, Name={other2.Name}");

输出结果:

Ini load Person: Age=30, Name=Sunny

保存的配置文件 Person.ini 在运行程序的根目录:

;<?Ini Version="NETool IniFile V1.0.1" Encoding="utf-8" Updated="2025-08-21 10:50:07"?>

[Sunny.Serialization.Demo.Person]
Age=30
Name=Sunny

内置支持的类型

  • 1 基础类型: bool, byte, sbyte, short, ushort, int, uint, long, ulong, System.Half, float, double, decimal, char;

  • 2 扩展类型: 原生的 string, DateTime, DateTimeOffset, Point, PointF, Size, SizeF, Color, Rectangle, RectangleF, TimeSpan, Guid, Version, Uri, TimeZoneInfo, CultureInfo 类型,System.NETool 扩展的 IpV4, IpV4Port, GuidV7, LngLat, SystemTime, SystemTime2 类型;

  • 3 枚举类型: enum;

  • 4 结构类型: struct, 标记为 [InlineArray(n)] 特性的结构体;

  • 5 结构类型: struct, 标记为 [StructLayout(LayoutKind.Explicit)] 特性的结构体;

  • 6 可序列化类: class, 标记为 [NEToolPackable] 特性的类,类必须增加 partial 关键字,并有无参数的构造函数;

  • 7 一维数组: T[], 其中 T 是上述 1、2、3、4、5 、6类型之一;

  • 8 列表:List<T>, ConcurrentList<T>, 其中 T 是上述 1、2、3、4、5 、6类型之一;

  • 9 字典:Dictionary<TKey, TValue>, ConcurrentDictionary<TKey, TValue>, 其中 TKey 和 TValue 是上述 1、2、3、4、5、6 类型之一;

类只有1 基础类型时,序列化与原生的 System.IO.BinaryWriter 完全一致

定义 [NEToolPackable] [IniConfig] class

[NEToolPackable] 和 [IniConfig] 特性注释 class 类,定义为 public 或者 internal,类必须增加 partial 关键字,并有无参数的构造函数。类不能为继承的子类。

类序列化的字段包含:

  • 字段 (filed) ,必须标记为 public,
  • 属性 (Property),必须标记为 public,并且有公开的 get;set;

标记为 [NEToolIgnore] 或者 [IgnoreDataMember] 或者带有其他 Ignore 特性的,序列化时将会忽略。

枚举生成 Description 扩展方法

NETool.Pack 会通过源生成 打上 EnumDescription 特性的枚举类的 Description 扩展方法,例如:

[EnumDescription]
public enum TestEnum : byte
{
    [Description("Excellent")]
    A = 0,
    [Description("Perfect")]
    B = 1,
    [Description("Good")]
    C = 2,
    [Description("Average")]
    D = 3,
    [Description("Failure")]
    E = 4,
    F = 5,
}

源生成的代码在 *.TestEnum.Desc.g.cs

/// <summary>
/// Sunny.Serialization.Demo.TestEnum 枚举的描述扩展方法
/// </summary>
public static class Sunny_Serialization_Demo_TestEnum_Description_Extensions
{
    /// <summary>
    /// 获取 Sunny.Serialization.Demo.TestEnum 枚举值的描述
    /// </summary>
    /// <param name="value">枚举值</param>
    /// <returns>枚举值的描述</returns>
    public static string Description(this Sunny.Serialization.Demo.TestEnum value)
    {
        return value switch
        {
            Sunny.Serialization.Demo.TestEnum.A => "Excellent",
            Sunny.Serialization.Demo.TestEnum.B => "Perfect",
            Sunny.Serialization.Demo.TestEnum.C => "Good",
            Sunny.Serialization.Demo.TestEnum.D => "Average",
            Sunny.Serialization.Demo.TestEnum.E => "Failure",
            Sunny.Serialization.Demo.TestEnum.F => "F",
            _ => string.Empty
        };
    }
}

枚举条目有 [Description] 特性的,输出特性描述文本。没有此特性的,输出枚举名称。

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on NETool.Generator:

Package Downloads
NETool.Pack

NETool.Pack 是 NETool 的二进制序列化器。

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.3.1 0 2/11/2026
4.3.0 0 2/11/2026
4.2.2 16 2/10/2026
4.2.1 35 2/9/2026
4.2.0 36 2/9/2026
4.1.9 34 2/6/2026
4.1.8 40 2/6/2026
4.1.7 40 2/6/2026
4.1.6 93 2/4/2026
4.1.5 93 2/3/2026
4.1.4 94 2/3/2026
4.1.3 95 2/2/2026
4.1.2 102 2/2/2026
4.0.7 101 2/2/2026
4.0.6 97 2/1/2026
4.0.5 109 2/1/2026
4.0.4 107 2/1/2026
4.0.3 96 1/30/2026
4.0.2 103 1/30/2026
4.0.1 143 1/30/2026
4.0.0 105 1/28/2026
3.9.5 100 1/28/2026
3.9.4 99 1/27/2026
3.9.3 102 1/25/2026
3.9.2 100 1/25/2026
3.9.1 101 1/24/2026
3.9.0 102 1/23/2026
3.8.9 97 1/23/2026
3.8.8 102 1/23/2026
3.8.7 112 1/22/2026
3.8.6 101 1/22/2026
3.8.5 101 1/22/2026
3.8.4 107 1/22/2026
3.8.3 99 1/21/2026
3.8.2 98 1/21/2026
3.8.1 100 1/21/2026
3.8.0 95 1/21/2026
3.7.1 101 1/21/2026
3.7.0 100 1/19/2026
3.6.9 103 1/19/2026
3.6.8 101 1/17/2026
3.6.7 117 1/17/2026
3.6.5 104 1/17/2026
3.6.4 107 1/17/2026
3.6.3 102 1/17/2026
3.6.2 94 1/17/2026
3.6.1 105 1/15/2026
3.6.0 107 12/31/2025
3.5.9 113 12/28/2025
3.5.8 113 12/27/2025
3.5.7 192 12/25/2025
3.5.6 191 12/24/2025
3.5.5 188 12/23/2025
3.5.4 190 12/23/2025
3.5.3 248 12/15/2025
3.5.2 166 12/14/2025
3.5.1 433 12/11/2025
3.5.0 448 12/10/2025
3.4.9 438 12/8/2025
3.4.8 440 12/8/2025
3.4.7 363 12/8/2025
3.4.6 197 12/5/2025
3.4.5 216 12/5/2025
3.4.4 220 12/4/2025
3.4.3 681 12/3/2025
3.4.2 696 12/3/2025
3.4.1 689 12/2/2025
3.4.0 189 11/28/2025
3.3.9 193 11/28/2025
3.3.8 202 11/27/2025
3.3.7 205 11/25/2025
3.3.6 392 11/21/2025
3.3.5 423 11/18/2025
3.3.4 359 11/17/2025
3.3.3 356 11/17/2025
3.3.2 181 11/15/2025
3.3.1 276 11/14/2025
3.3.0 303 11/12/2025
3.2.6 295 11/11/2025
3.2.5 252 11/10/2025
3.2.4 215 11/7/2025
3.2.3 224 11/6/2025
3.2.2 174 10/31/2025
3.2.1 214 10/30/2025
3.2.0 226 10/30/2025
3.1.4 215 10/30/2025
3.1.3 221 10/29/2025
3.1.2 211 10/29/2025
3.1.1 210 10/28/2025
3.1.0 202 10/28/2025
3.0.9 210 10/28/2025
3.0.8 168 10/24/2025
3.0.7 216 10/19/2025
3.0.6 151 10/18/2025
3.0.5 143 10/18/2025
3.0.4 155 10/17/2025
3.0.3 203 10/16/2025
3.0.2 194 10/15/2025
3.0.1 207 10/13/2025
3.0.0 184 10/12/2025
2.9.9 150 10/12/2025
2.9.8 148 10/11/2025
2.9.7 179 10/10/2025
2.9.6 197 10/10/2025
2.9.5 202 10/8/2025
2.9.4 203 10/8/2025
2.9.3 219 10/7/2025
2.9.2 208 9/29/2025
2.9.1 178 9/28/2025
2.9.0 176 9/28/2025
2.8.9 184 9/28/2025
2.8.8 140 9/27/2025
2.8.7 208 9/26/2025
2.8.6 212 9/25/2025
2.8.5 196 9/24/2025
2.8.4 209 9/24/2025
2.8.3 202 9/24/2025
2.8.2 198 9/23/2025
2.8.1 196 9/23/2025
2.8.0 216 9/23/2025
2.7.9 217 9/20/2025
2.7.8 271 9/19/2025
2.7.7 340 9/18/2025
2.7.6 320 9/17/2025
2.7.5 336 9/17/2025
2.7.4 323 9/17/2025
2.7.3 339 9/17/2025
2.7.2 288 9/15/2025
2.7.1 279 9/15/2025
2.7.0 198 9/11/2025
2.6.9 188 9/10/2025
2.6.8 183 9/10/2025
2.6.7 185 9/8/2025
2.6.6 182 9/8/2025
2.6.5 165 9/5/2025
2.6.3 168 9/5/2025
2.6.2 200 9/4/2025
2.6.1 192 9/2/2025
2.6.0 189 9/1/2025
2.5.9 223 8/29/2025
2.5.8 243 8/29/2025
2.5.7 227 8/28/2025
2.5.6 297 8/25/2025
2.5.5 140 8/22/2025
2.5.3 194 8/21/2025
1.0.2 181 8/21/2025
1.0.1 183 8/21/2025
1.0.0 190 8/21/2025
0.8.0 184 8/20/2025
0.7.2 165 8/20/2025
0.7.1 187 8/19/2025
0.7.0 190 8/14/2025
0.6.3 190 8/14/2025
0.6.2 194 8/12/2025
0.6.1 187 8/12/2025
0.6.0 193 8/12/2025
0.5.5 191 8/11/2025
0.5.3 199 8/11/2025
0.5.2 173 8/9/2025
0.5.1 254 8/7/2025
0.5.0 269 8/7/2025
0.4.0 269 8/6/2025
0.3.0 266 8/5/2025
0.2.0 268 8/5/2025
0.1.0 211 8/4/2025