Skip to content

进阶用法

本页描述了Nino的进阶用法

重命名

Nino支持在序列化后重命名类型名称和命名空间。这意味着你可以在序列化后重命名类型名称和命名空间,而仍然可以反序列化旧类型名称和命名空间序列化的旧数据。这个功能在v3.2.2中实现,在此之前的版本中,重命名类型名称和命名空间会导致反序列化失败。

使用此功能,你需要在要重命名的类型上添加[NinoFormerName]标签,并在标签中指定旧的类型名称和命名空间。

INFO

旧的类型名称和命名空间来自NinoTypeConst.g.cs的旧版本,只需从文件中复制所需的名称并粘贴到属性中即可。 对于泛型类型,只需要指定旧类型名称和命名空间,而不是泛型类型参数。

csharp
// <auto-generated/>

using System;

namespace Nino.UnitTests.NinoGen
{
    public static class NinoTypeConst
    {
        ...
		// global::Nino.UnitTests.Generic<int>
		public const int Nino_UnitTests_Generic_int_ = -388704858;
		// global::Nino.UnitTests.Generic<string>
		public const int Nino_UnitTests_Generic_string_ = -1996567692;
		// global::ListElementClass2Renamed
		public const int ListElementClass2Renamed = 329143115;
        ...

示例

csharp
[NinoType]
[NinoFormerName("global::Nino.UnitTests.Generic")]
public class Generic<T> 
public class GenericRenamed<T> 
{
    ...
}

[NinoType]
[NinoFormerName("global::ListElementClass2")]
public class ListElementClass2
public class ListElementClass2Renamed 
{
    ...
}

字符串编码

Nino支持为NinoType中的字符串成员指定编码。默认情况下,Nino使用UTF-16编码来序列化和反序列化这些字符串成员。然而,你可以通过使用[NinoUtf8]属性来指定不同的编码。使用UTF-8编码的好处是可以减小序列化数据的大小(将序列化的二进制大小减半)。

WARNING

使用UTF-8编码可能会导致序列化和反序列化这些字段的性能略有下降,大约在5%到10%之间。

INFO

当使用[NinoUtf8]标记非字符串成员时,Nino会忽略这个属性。

示例

csharp
[NinoType]
public class StringData
{
    [NinoUtf8] public string Str;
    [NinoUtf8] public bool ShouldHaveNoEffect;
}

版本兼容性

  • 重命名已序列化的字段/属性是允许的(但必须确保成员顺序保持不变,或 NinoMember 中指定的 id 保持一致)
  • 更改已序列化字段/属性的(非托管 结构体)类型为具有相同内存大小的 非托管 结构体是允许的(例如:int -> uintint -> floatList<long> -> List<double>List<int[]> -> List<float[]>
  • 更改已序列化字段/属性的类型为其 基类/派生类是允许的
  • 在数据结构末尾添加新字段/属性是允许的(例如,在使用 [NinoType] 属性进行 自动收集 时,将 新成员放在之前收集成员的 最后一个成员之后;或者如果使用 [NinoType(false)][NinoMember(id)] 属性,则将 id 设置为一个合理的值,以便其 排在之前成员的最后一个之后,Nino 会按照 id升序对成员进行排序)

    需要在项目中定义符号 WEAK_VERSION_TOLERANCE 以启用此功能

  • 删除已序列化字段/属性是不允许的

WARNING

  • 使用了WEAK_VERSION_TOLERANCE定义的项目无法反序列化由未使用WEAK_VERSION_TOLERANCE定义的项目序列化的数据,反之亦然

示例

有效更改:

csharp
[NinoType]
public class SampleClass
{
    // float和int都是4字节
    public List<float> Data; 
    public List<int> Data; 
}
csharp
[NinoType]
public interface IBase
{
}
[NinoType]
public class Derived : IBase;
[NinoType]
public class SampleClass2
{
    public Derived Data; 
    public IBase Data; 
}
csharp
[NinoType]
public interface IBase
{
}
[NinoType]
public class Derived : IBase;
[NinoType]
public struct DerivedStruct : IBase;
[NinoType]
public class SampleClass2
{
    public IBase Data; 
    // 如果Data是DerivedStruct
    public DerivedStruct Data; 
    // 如果Data是Derived
    public Derived Data; 
}

无效更改:

csharp
[NinoType]
public class SampleClass2
{
public SampleClass Data; 
// 假设SampleClass2不是SampleClass的基类或派生类
public SampleClass2 Data; 
}
csharp
[NinoType]
public interface IBase
{
}
[NinoType]
public class Derived : IBase;
[NinoType]
public struct DerivedStruct : IBase;
[NinoType]
public class SampleClass2
{
    public IBase Data; 
    // 如果Data是DerivedStruct
    public Derived Data; 
    // 如果Data是Derived
    public DerivedStruct Data; 
}
csharp
[NinoType]
public class SampleClass2
{
public int Data; 
public long Data; 
}

有效添加新成员:

csharp
// 假设WEAK_VERSION_TOLERANCE已定义
[NinoType]
public class SampleClass
{
    public int Id;
    public string Name;
    public bool Extra; 
}
csharp
// 假设WEAK_VERSION_TOLERANCE已定义
[NinoType(false)]
public class SampleClass
{
    [NinoMember(0)] public int Id;
    [NinoMember(1)] public string Name;
    [NinoMember(2)] public bool Extra; 
}

无效添加新成员:

csharp
// 假设WEAK_VERSION_TOLERANCE未定义
[NinoType]
public class SampleClass
{
    public int Id;
    public string Name;
    public bool Extra; 
}
csharp
// 假设WEAK_VERSION_TOLERANCE已定义
[NinoType]
public class SampleClass
{
    public int Id;
    public bool Extra; 
    public string Name;
}
csharp
// 假设WEAK_VERSION_TOLERANCE已定义
[NinoType(false)]
public class SampleClass
{
    [NinoMember(0)] public int Id;
    [NinoMember(1)] public string Name;
    [NinoMember(-1)] public bool Extra; 
}

自定义构造函数

Nino现在支持使用自定义构造函数来反序列化一个nino序列化的对象。这在你想要在反序列化对象时执行额外操作时非常有用,比如初始化那些没有被序列化的字段或属性。

另外,这对于record来说,nino可以使用主构造函数或自定义构造函数来反序列化record。

使用方法

以下属性使用Field1, Field2, ...作为构造函数的参数来反序列化对象。

csharp
[NinoConstructor(nameof(Field1), nameof(Field2), ...)]

对于record,Nino将优先使用主构造函数来反序列化对象。如果不想使用主构造函数,可以使用[NinoConstructor]属性来指定自定义构造函数来反序列化对象。

对于class,Nino将自动使用第一个public构造函数来反序列化对象。其中构造函数要么是无参数的,要么所有参数与类中要反序列化的字段或属性的名称相同。如果不想使用上述的构造函数进行反序列化,可以使用[NinoConstructor]属性来指定自定义构造函数来反序列化对象。

对于struct,Nino将自动使用无参数构造函数来反序列化对象。如果struct不应该使用无参数构造函数(需要注意,struct始终有无参数构造函数),可以使用[NinoConstructor]属性来指定自定义构造函数来反序列化对象。

限制

  • 自定义构造函数必须是public构造函数。
  • 自定义构造函数的参数数量必须与NinoConstructor标签中的参数数量相同。
  • NinoConstructor标签的参数必须与类、struct或record中会被反序列化的字段或属性的名称匹配(忽略大小写)
  • NinoConstructor标签的参数表示这个类型的对应会被反序列化的成员的名称,会按顺序传递给构造函数。

示例

csharp
[NinoType]
public record SimpleRecord
{
    public int Id;
    public string Name;
    public DateTime CreateTime;

    public SimpleRecord()
    {
        Id = 0;
        Name = string.Empty;
        CreateTime = DateTime.MinValue;
    }

    [NinoConstructor(nameof(Id), nameof(Name))]
    public SimpleRecord(int id, string name)
    {
        Id = id;
        Name = name;
        CreateTime = DateTime.Now;
    }
}
csharp
[NinoType]
public record SimpleRecord2(int Id, string Name, DateTime CreateTime);

[NinoType]
public record SimpleRecord4(int Id, string Name, DateTime CreateTime) // 会被使用
{
    [NinoIgnore] public bool Flag;

    public int ShouldNotIgnore;

    // Nino 不会使用这个构造函数
    public SimpleRecord4() : this(0, "", DateTime.MinValue)
    {
    }
}
csharp
[NinoType]
public record SimpleRecord5(int Id, string Name, DateTime CreateTime) // 不会被使用
{
    [NinoIgnore] public bool Flag;

    public int ShouldNotIgnore;

    // Nino会使用这个构造函数,但是这么做不好,因为在反序列化时会忽略序列化的Id, Name, CreateTime
    [NinoConstructor]
    public SimpleRecord5() : this(0, "", DateTime.MinValue)
    {
    }
}

除此之外,我们不仅可以针对record使用自定义构造函数,也可以针对classstruct使用自定义构造函数。

csharp
[NinoType]
public struct SimpleStruct
{
    public int Id;
    public string Name;
    public DateTime CreateTime;
    
    // 会使用这个构造函数来创建结构体
    [NinoConstructor(nameof(Id), nameof(Name), nameof(CreateTime))]
    public SimpleStruct(int a, string b, DateTime c)
    {
        Id = a;
        Name = b;
        CreateTime = c;
    }
}
csharp
[NinoType]
public class SimpleClassWithConstructor
{
    public int Id;
    public string Name;
    public DateTime CreateTime;
    
    // [NinoConstructor(nameof(Id), nameof(Name), nameof(CreateTime))] - 我们尝试不使用这个,看看是否会自动使用
    // 应该会自动使用这个构造函数,因为这是唯一的public构造函数
    public SimpleClassWithConstructor(int id, string name, DateTime createTime)
    {
        Id = id;
        Name = name;
        CreateTime = createTime;
    }
}