NETool.Generator 4.3.2

dotnet add package NETool.Generator --version 4.3.2
                    
NuGet\Install-Package NETool.Generator -Version 4.3.2
                    
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.2">
  <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.2" />
                    
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.2
                    
#r "nuget: NETool.Generator, 4.3.2"
                    
#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.2
                    
#: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.2
                    
Install as a Cake Addin
#tool nuget:?package=NETool.Generator&version=4.3.2
                    
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.2 101 2/12/2026
4.3.1 98 2/11/2026
4.3.0 101 2/11/2026
4.2.2 99 2/10/2026
4.2.1 103 2/9/2026
4.2.0 103 2/9/2026
4.1.9 99 2/6/2026
4.1.8 106 2/6/2026
4.1.7 105 2/6/2026
4.1.6 103 2/4/2026
4.1.5 101 2/3/2026
4.1.4 101 2/3/2026
4.1.3 98 2/2/2026
4.1.2 106 2/2/2026
4.0.7 106 2/2/2026
4.0.6 101 2/1/2026
4.0.5 113 2/1/2026
4.0.4 111 2/1/2026
4.0.3 100 1/30/2026
4.0.2 106 1/30/2026
Loading failed