NETool.Pack 2.5.3

There is a newer version of this package available.
See the version list below for details.
dotnet add package NETool.Pack --version 2.5.3
                    
NuGet\Install-Package NETool.Pack -Version 2.5.3
                    
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.Pack" Version="2.5.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NETool.Pack" Version="2.5.3" />
                    
Directory.Packages.props
<PackageReference Include="NETool.Pack" />
                    
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.Pack --version 2.5.3
                    
#r "nuget: NETool.Pack, 2.5.3"
                    
#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.Pack@2.5.3
                    
#: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.Pack&version=2.5.3
                    
Install as a Cake Addin
#tool nuget:?package=NETool.Pack&version=2.5.3
                    
Install as a Cake Tool

SunnyUI.Net

NETool.Pack

介绍

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

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

软件架构

.Net8, .Net9, .Net10

安装教程

Nuget 安装最新版本。

Install-Package NETool.Pack

快速入门

定义要二进制序列化的类,并使用属性和关键字对其进行注释。[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; }

    [NEToolPackIgnore]
    [IniConfigIgnore]
    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-08-21 14:48:50/>
// 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
{
    ///<inheritdoc/>
    public void SaveIni(string fileName)
    {
        var writer = __CreateIniWriter(fileName);
        writer.SaveAs(fileName);
    }

    ///<inheritdoc/>
    public Task SaveIniAsync(string fileName)
    {
        var writer = __CreateIniWriter(fileName);
        return writer.SaveAsAsync(fileName);
    }

    ///<inheritdoc/>
    public void SaveIni()
    {
        SaveIni(FileEx.VerifyFileName("Person.ini"));
    }

    ///<inheritdoc/>
    public Task SaveIniAsync()
    {
        return SaveIniAsync(FileEx.VerifyFileName("Person.ini"));
    }

    ///<inheritdoc/>
    public void LoadIni(string fileName)
    {
        var reader = new IniFile(fileName, Encoding.UTF8, false);
        reader.Load();
        __LoadFromIniReader(reader);
    }

    ///<inheritdoc/>
    public async Task LoadIniAsync(string fileName)
    {
        var reader = new IniFile(fileName, Encoding.UTF8, false);
        await reader.LoadAsync().ConfigureAwait(false);
        __LoadFromIniReader(reader);
    }

    ///<inheritdoc/>
    public void LoadIni()
    {
        LoadIni(FileEx.VerifyFileName("Person.ini"));
    }

    ///<inheritdoc/>
    public Task LoadIniAsync()
    {
        return LoadIniAsync(FileEx.VerifyFileName("Person.ini"));
    }

    /// <summary>
    /// 创建一个 IniFile 写入器
    /// </summary>
    /// <param name="fileName">配置文件名</param>
    /// <returns>IniFile 写入器</returns>
    private IniFile __CreateIniWriter(string fileName)
    {
        var writer = new IniFile(fileName, Encoding.UTF8, false);
        #region 序列化
        var session = "Sunny.Serialization.Demo.Person";
        // int Age
        writer.WriteInt32(session, "Age", this.Age);
        // string Name
        writer.WriteString(session, "Name", this.Name);
        #endregion 序列化
        return writer;
    }

    /// <summary>
    /// 从 IniFile 读取器加载
    /// </summary>
    /// <param name="reader">IniFile 读取器</param>
    private void __LoadFromIniReader(IniFile reader)
    {
        #region 反序列化
        var session = "Sunny.Serialization.Demo.Person";
        // int Age
        this.Age = reader.ReadInt32(session, "Age", this.Age);
        // string Name
        this.Name = reader.ReadString(session, "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, IpV4Point, GuidV7, LngLat, SystemTime, MiniSystemTime 类型;

  • 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;

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

枚举生成 Description 扩展方法

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

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.

NuGet packages (1)

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

Package Downloads
AllUI

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.6.1 0 9/2/2025
2.6.0 22 9/1/2025
2.5.9 145 8/29/2025
2.5.8 152 8/29/2025
2.5.7 156 8/28/2025
2.5.6 239 8/25/2025
2.5.5 81 8/22/2025
2.5.3 128 8/21/2025
1.0.2 122 8/21/2025
1.0.1 123 8/21/2025
1.0.0 119 8/21/2025
0.8.0 123 8/20/2025
0.7.2 124 8/20/2025
0.7.1 123 8/19/2025
0.7.0 137 8/14/2025
0.6.3 135 8/14/2025
0.6.2 139 8/12/2025
0.6.1 139 8/12/2025
0.6.0 139 8/12/2025
0.5.5 137 8/11/2025
0.5.3 138 8/11/2025
0.5.2 121 8/9/2025
0.5.1 224 8/7/2025
0.5.0 217 8/7/2025
0.4.0 223 8/6/2025
0.3.0 224 8/5/2025
0.2.0 208 8/5/2025