Skip to content

Conversation

kengwang
Copy link
Contributor

@kengwang kengwang commented Sep 16, 2025

Motivation

Since the PbElem field of CommonElem(53) contains actual data, the ServiceType field is used to distinguish the type of data contained therein.

The TLVs also have this design model style.

It is hoped that the data structure can reflect this model design, making it easier to convert during serialization and deserialization.

Current

(Not Implemented CommonElem in V2)
In Lagrange.Core, directly read to a byte array and re-serialize.

Reference

Reference STJ's JsonDerivedTypeAttribute and JsonPolymorphicAttribute structures to achieve polymorphism.

Example

See Lagrange.Proto.Test/ProtoPolymorphismTest.cs

[ProtoPolymorphic(FieldNumber = 1)]
[ProtoDerivedType(typeof(DerivedClassA), 2)]
[ProtoDerivedType(typeof(DerivedClassB), 3)]
public class BaseClass
{
    public BaseClass() : this(-1) { }

    public BaseClass(int identifier)
    {
        IdentifierProperty = identifier;
    }

    [ProtoMember(1)] public int IdentifierProperty { get; set; }
}

public class DerivedClassA() : BaseClass(2)
{
    [ProtoMember(2)] public string NameProperty { get; set; }
}

public class DerivedClassB() : BaseClass(3)
{
    [ProtoMember(2)] public float ValueProperty { get; set; }
}

BaseClass original = new DerivedClassA { NameProperty = "TestName" };
byte[] bytes = ProtoSerializer.Serialize(original);
BaseClass deserialized = ProtoSerializer.Deserialize<BaseClass>(bytes);

To-do

  • Implement in Lagrange.Proto.Generator
  • Support multi-level inheritance
  • Optimize code performance

Just a crazy idea

@kengwang
Copy link
Contributor Author

Currently, it doesn't support multi-level inheritance. still improving.

Try to go to the root type first and then (un)wrap the protobuf type discriminator step by step, going down to the current type.

@kengwang kengwang marked this pull request as ready for review October 5, 2025 16:47
@Copilot Copilot AI review requested due to automatic review settings October 5, 2025 16:47
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements polymorphic serialization support for protobuf-style serialization in the Lagrange.Proto library. The implementation allows base classes to define derived type mappings using attributes, enabling automatic serialization/deserialization of the correct derived type based on a discriminator field.

  • Added polymorphic attribute system (ProtoPolymorphicAttribute and ProtoDerivedTypeAttribute) for defining type hierarchies
  • Implemented runtime polymorphic type resolution during serialization and deserialization
  • Extended the source generator to emit polymorphic-aware serialization code

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
ThrowHelper.cs Added error handling methods for polymorphic serialization scenarios
ProtoSerializer.Serialize.cs Enhanced serialization logic to handle polymorphic types with discriminator fields
ProtoSerializer.Helpers.cs Added helper methods for dynamic converter creation and reflection-based type info
ProtoSerializer.Deserialize.cs Extended deserialization to support polymorphic type reconstruction
ProtoTypeResolver.Dynamic.cs Added polymorphic metadata population and multi-level inheritance support
ProtoPolymorphicInfo.cs New metadata classes for managing polymorphic type information
ProtoObjectInfo.cs Added polymorphic info property to object metadata
ProtoPolymorphicAttribute.cs New attribute for configuring polymorphic behavior
ProtoDerivedTypeAttribute.cs New attribute for registering derived types with discriminators
ProtoPolymorphismTest.cs Comprehensive test suite covering various polymorphic scenarios
Program.cs Added example classes demonstrating polymorphic usage
ProtoSourceGenerator.Parser.cs Enhanced parser to handle polymorphic attributes and inheritance
ProtoSourceGenerator.Emitter.TypeInfo.cs Extended code generation for polymorphic type descriptors
ProtoSourceGenerator.Emitter.Serialize.cs Added polymorphic-aware serialization code generation
PolymorphicTypeInfo.cs New entity classes for representing polymorphic metadata

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

ProtoTypeResolver.Register(converter = new ProtoObjectConverter<T>());
// has polymorphic type
var index = polymorphicInfo.PolymorphicIndicateIndex;
var fieldInfo = fields.FirstOrDefault(t=>t.Value.Field == index);
Copy link

Copilot AI Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Missing space around the lambda arrow operator. Should be t => t.Value.Field == index for consistency with coding standards.

Suggested change
var fieldInfo = fields.FirstOrDefault(t=>t.Value.Field == index);
var fieldInfo = fields.FirstOrDefault(t => t.Value.Field == index);

Copilot uses AI. Check for mistakes.


// get creator and fields, oh my reflection!
var polyObjectInfo = polyConverter.GetType()
.GetField("ObjectInfo",BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(polyConverter)!;
Copy link

Copilot AI Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Missing space after the comma in BindingFlags parameter. Should be BindingFlags.NonPublic | BindingFlags.Instance with proper spacing.

Suggested change
.GetField("ObjectInfo",BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(polyConverter)!;
.GetField("ObjectInfo", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(polyConverter)!;

Copilot uses AI. Check for mistakes.

}
}

private void EmitFieldsInfo(SourceWriter source,Dictionary<int, ProtoFieldInfo> fields )
Copy link

Copilot AI Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Missing space after comma in parameter list. Should be SourceWriter source, Dictionary<int, ProtoFieldInfo> fields.

Suggested change
private void EmitFieldsInfo(SourceWriter source,Dictionary<int, ProtoFieldInfo> fields )
private void EmitFieldsInfo(SourceWriter source, Dictionary<int, ProtoFieldInfo> fields )

Copilot uses AI. Check for mistakes.


}

private void PopulateFieldInfo(INamedTypeSymbol classSymbol, Dictionary<int, ProtoFieldInfo> fields, string identifier = "" ,CancellationToken token = default)
Copy link

Copilot AI Oct 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Extra space before comma in parameter list. Should be string identifier = \"\", CancellationToken token = default).

Suggested change
private void PopulateFieldInfo(INamedTypeSymbol classSymbol, Dictionary<int, ProtoFieldInfo> fields, string identifier = "" ,CancellationToken token = default)
private void PopulateFieldInfo(INamedTypeSymbol classSymbol, Dictionary<int, ProtoFieldInfo> fields, string identifier = "", CancellationToken token = default)

Copilot uses AI. Check for mistakes.

@kengwang kengwang closed this Oct 10, 2025
@kengwang kengwang reopened this Oct 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant