Skip to content

Use source generators to remove the boiler plate of creating encodeable objects #3606

@romanett

Description

@romanett

Discussed in #3605

Originally posted by lkomanetz March 9, 2026
Right now I use Claude to generate the boiler plate involved in inherited from EncodeableObject. It would be wonderful if there was a source generator that would handle, at least, overriding Decode, Encode, and IsEqual. I wouldn't mind if it was done by convention or through attributes (like the CommunityToolkit.Mvvm package does for view models). I could see it a couple of ways.

Convention

public partial class MyEncodeableObject : EncodeableObject
{
    private long p_BigNumber; // The p_ tells the source generator that it is an OPC-UA field

    public override ExpandedNodeId TypeId => ExpandedNodeId.Parse("node_id");
    public override ExpandedNodeId BinaryEncodingId => ExpandedNodeId.Parse("node_id");
    public override ExpandedNodeId XmlEncodingId => ExpandedNodeId.Parse("node_id");
}

// What it generates
public partial class MyEncodeableObject : EncodeableObject
{
    public long BigNumber
    {
        get => p_BigNumber;
        set -> p_BigNumber = value;
    }

    public override void Decode(IDecoder decoder)
    {
        BigNumber = decoder.ReadInt64("BigNumber");
    }

    public override void Encode(IEncoder encoder)
    {
        encoder.WriteInt64("BigNumber", BigNumber);
    }

    public override bool IsEqual(IEncodeable encodeable)
    {
        if (encodeable is not MyEncodeableObject other)
        {
            return false;
        }

        if (ReferenceEquals(this, encodeable))
        {
            return true;
        }

        return (
            BigNumber == other.BigNumber
        );
    }
}

And now the Attribute style of handling it (which might provide some more flexibility

Attributes

[Encodeable(TypeId = "node_id", BinaryEncodingId = "node_id", XmlEncodingId = "node_id")]
public partial class MyEncodeableObject : EncodeableObject
{
    [UaProperty(Order=2)] // Use the property name by convention
    private long _bigNumber;

    [UaProperty(Order=1, Name = "ErrorMessage")] // Use the provided UDT property name
    private string _someText;
}

This would basically generate the same thing (adding the ExpandedNodeId properties that the other one wouldn't) but also use information in the attribute to know in what order to read from the decoder and write to the encoder. I can continue to use AI tools to do it but a source generator would be the superior method to handling the boiler plate in this case. I'd love to hear others' thoughts on this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementAPI or feature enhancement

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions