Skip to content

Give semantic, vector and colour members their default in generated C# [patch] - #227

Merged
matt-edmondson merged 1 commit into
mainfrom
fix/218-semantic-vector-defaults
Sep 29, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
fix/218-semantic-vector-defaults

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #218

What was wrong

CSharpCodeGenerator.DefaultInitialiserFor only had arms for Int, Long, Float, Double, Bool, String and Enum. Validation also accepts a NumberDefault on a Semantic member (reading through to its representation) and on Vector2/3/4 and the colours. For those members the SchemaDefault attribute was written but no initialiser was, so new Body().Mass came out as 0 in C# while the C++ output started it at 2.5. The round-trip tests only read the attribute back, so they didn't catch it.

Change

DefaultInitialiserFor now delegates to DefaultLiteralFor(value, type), which builds the default as an expression of the member's type:

  • Semantic: builds the representation's literal with the existing rules, then wraps it in the semantic type's explicit conversion, e.g. (Kilograms)(2.5f). The inner value is parenthesised so a negative default doesn't parse as subtraction. A refinement chain such as Heavy : Kilograms : Float works because Representation() already walks it. A chain that never reaches a real type falls through, as validation already reports that case.
  • Vector2/3/4 and ColorRGB/RGBA: the number goes in every component, which matches the C++ side's reading. Examples: new System.Numerics.Vector3(1f, 1f, 1f), new ktsu.Schema.Runtime.Vector2<int>(2, 2), new ktsu.Schema.Runtime.ColorRgba(0.5f, …).
  • Existing arms: unchanged, apart from now returning a bare expression that DefaultInitialiserFor wraps in = …;.
  • TravelsAsBytes structs: they still get their parameterless constructor, because that check keys off InitialiserFor(member) being non-empty, and the new arms are included in that.

Out of scope: a NumberDefault on a Quantity member still gets no C# initialiser. Constructing a ktsu.Semantics.Quantities value is a separate question from this issue.

Tests

CodeGenerationRoundTripTests.TestSemanticVectorAndColourMembersStartAtTheirDefaults runs once for a class and once for a TravelsAsBytes struct. It compiles a class with these defaulted members:

  • a semantic over Float (2.5 and −1.5)
  • a semantic over Int
  • a refining semantic (Heavy : Kilograms)
  • a float Vector3
  • an int Vector2
  • a ColorRGBA

It then constructs an instance and checks each member's runtime value, not just the attribute.

With the CSharpCodeGenerator.cs change reverted, both rows fail with Expected values to be equal. With the change, the full Schema.Test suite passes on net10.0 (504/504). I couldn't run the net8.0/net9.0 legs because those runtimes aren't installed in this environment. The only dotnet format findings in the changed range are IDE0001 on the two pre-existing Models.Types.String/Enum arms I moved. That qualification style is used throughout the file.

This PR and #226 (for #217) both touch CSharpCodeGenerator.cs, in different methods. The String arm here still calls Quote, so whichever merges second should rebase cleanly.

🤖 Generated with Claude Code

https://claude.ai/code/session_011PMSp3sjbza8Td6fxS2Kjr


Generated by Claude Code

…# [patch]

DefaultInitialiserFor only had arms for Int, Long, Float, Double, Bool,
String and Enum, so a NumberDefault on a Semantic, Vector2/3/4 or colour
member - all of which validation accepts - was written to the
SchemaDefault attribute but got no initialiser, and a new instance started
at zero where the C++ output started at the default.

Build the default as a typed expression: a semantic type through its
explicit conversion from the representation's literal, and a vector or
colour with the number in every component, matching the C++ generator.
A TravelsAsBytes struct gets its parameterless constructor as before,
since that keys off the initialiser being non-empty.

Fixes #218

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011PMSp3sjbza8Td6fxS2Kjr
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 863b98a into main Sep 29, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the fix/218-semantic-vector-defaults branch September 29, 2026 07:10
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.

C# generator drops the default on Semantic and Vector members: new Body().Mass is 0 in C# but 2.5 in C++

2 participants