[pull] master from angularsen:master - #47
Open
pull[bot] wants to merge 98 commits into
Open
Conversation
Adding two units used in our domain for small pressure values in two different systems of measurement (metric and US customary).
Add the grams force unit
Closes #1647 nanoFramework support has moved to its own repository, [nanoframework/nanoFramework.UnitsNet](https://github.com/nanoframework/nanoFramework.UnitsNet), which now publishes its own NuGet packages. This removes the in-repo nanoFramework stack. ## Changes - **Projects**: removed the entire `UnitsNet.NanoFramework/` directory (projects + generated code). - **Code generation**: removed `CodeGen/Generators/NanoFrameworkGen/`, `NanoFrameworkGenerator.cs`, `NanoFrameworkVersions.cs`, and the `skipNanoFramework`/`updateNanoFrameworkDependencies` wiring in `Program.cs`. - **Build scripts**: removed nanoFramework build/pack/version logic from `Build/build.ps1`, `build-functions.psm1`, `init.ps1`, `set-version-UnitsNet.ps1`, and deleted `build-pack-nano-nugets.psm1`. `init.ps1` no longer downloads NuGet.exe or the VS nanoFramework extension. - **CI**: dropped the nanoFramework setup steps and `-IncludeNanoFramework` flag from GitHub Actions (`ci.yml`, `pr.yml`) and Azure Pipelines. - **Misc**: deleted redundant `build-all-targets.bat` (now identical to `build.bat`), `upgrade-nanoframework.sh`, `Docs/nanoframework.md`; cleaned up `README.md`, `AGENTS.md`, `Docs/README.md`, `cSpell.json`, and the ReSharper dictionary. External-port references (the ports table and dependent-projects list in README) are intentionally kept, since the package still exists in its new home. ## Verification - `dotnet build CodeGen` → 0 warnings, 0 errors - `dotnet run --project CodeGen` → regenerates cleanly, no nanoFramework output - `dotnet build UnitsNet.slnx -c Release` → 0 errors (only pre-existing obsolete-API benchmark warnings) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Summary Fixes the Azure Pipelines build failure introduced by #1657: \`\`\` error CS0266: Cannot implicitly convert type 'UnitsNet.UnitInfo<TUnit>' to 'UnitsNet.UnitInfo<TQuantity, TUnit>' \`\`\` at [UnitsNet/Extensions/QuantityExtensions.cs:47](UnitsNet/Extensions/QuantityExtensions.cs:47): \`\`\`csharp return quantity.QuantityInfo[quantity.Unit]; \`\`\` ## Why this happens The receiver is statically `IQuantity<TQuantity, TUnit>`, whose `QuantityInfo` getter is shadowed (`new`) to return the more specific `QuantityInfo<TQuantity, TUnit>`. On the hosted Azure Pipelines image's Roslyn build, member lookup resolves to the inherited `IQuantity<TUnit>.QuantityInfo` (`QuantityInfo<TUnit>`) instead, so the indexer returns `UnitInfo<TUnit>` and the conversion to `UnitInfo<TQuantity, TUnit>` fails. Local builds on newer SDKs happened to resolve the shadowed member and compiled cleanly, which is why #1657's CI never tripped on the maintainer's machine. ## Fix The runtime value is always the more derived type, so cast through `QuantityInfo<TQuantity, TUnit>` explicitly to surface the typed indexer. No behavior change. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… interfaces (#1675) Per discussion on #1657, this introduces a static abstract `Info` member on the IQuantityOfType<TQuantity> and IQuantity<TSelf,TUnitType> interfaces under #if NET, marks the existing instance QuantityInfo property as [Obsolete] on .NET 5+, and adds GetQuantityInfo() extension methods on QuantityExtensions so callers have a single discoverable API that works on every TFM. Why - The instance QuantityInfo property invariably returns a per-type static value. Exposing it as an instance member implies it can vary per instance, which it cannot, and incurs interface dispatch (boxing on structs) for every call. - The static abstract member lets generic algorithms reach the info with `TSelf.Info` directly, no boxing, no virtual call. - The extension method pair (`GetQuantityInfo()` / `GetQuantityInfo<TUnit>()`) is the discoverable replacement for callers that only have an `IQuantity` reference. It looks the quantity up via `UnitsNetSetup.Default.Quantities`. - Keeping the instance property obsolete (warning) instead of removing it preserves source compatibility for existing callers and the netstandard2.0 contract. We can promote to error / remove once netstandard2.0 is dropped. Implementation notes - Generated quantities already expose `public static QuantityInfo<TSelf, TUnitType> Info { get; }`, which directly satisfies the typed static abstract. The non-generic `IQuantityOfType<TSelf>.Info` is satisfied by a default static implementation in IQuantity<TSelf,TUnitType>: `static QuantityInfo IQuantityOfType<TSelf>.Info => TSelf.Info;`. No codegen change required. - The IQuantity bridge `QuantityInfo IQuantity.QuantityInfo => QuantityInfo;` chain inside the interfaces uses #pragma to suppress the obsolete warning on the bridge itself. - Internal callers in UnitsNet were migrated to either `TSelf.Info` / `TSelf.From` (where the generic constraint allows) or `quantity.GetQuantityInfo()` (where it doesn't). Callers that must keep working for custom quantities not registered in `UnitsNetSetup.Default` (JsonNet serialization, debugger proxy, QuantityTypeConverter) keep using the instance member with a `#pragma warning disable CS0618` and a comment explaining why. - HowMuch test custom quantity changed `public static readonly` field to `public static QuantityInfo Info { get; }` property to satisfy the static abstract. - Tests added for round-trip equivalence between `Mass.Info`, `mass.GetQuantityInfo()`, `TQuantity.Info` (via static abstract on IQuantityOfType<T>), and `TSelf.Info` (via static abstract on IQuantity<TSelf,TUnit>). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fork pull request workflows cannot request the OIDC token required by the Claude action. Skip these automatic reviews to avoid failed checks and limit exposure to untrusted PR input. Maintainers can still request a review by commenting `@claude`.
Standard for paper grammage/base weight in North America is lbs/MSF (pound per 1000 square feet) instead of g/m². Conversion factor is standardized by TAPPI (free sample of TIP 0800-01 can be found [here](https://www.normsplash.com/Samples/TAPPI/145775521/TAPPI-TIP-0800-01-2012-en.pdf)). I also added lbs/ft² for completeness. --------- Co-authored-by: Andreas Fischer <andreasfische@bhs-intralogistics.com> Co-authored-by: Andreas Gullberg Larsen <andreas.larsen84@gmail.com>
Adds additional BTU-based units for power density, thermal conductivity, and heat transfer coefficient. ## Changes - Added `BtuPerSecondCubicInch` and `BtuPerSecondCubicFoot` to `PowerDensity`. - Added `BtuPerSecondInchFahrenheit` to `ThermalConductivity`. - Added `BtuPerSecondSquareInchDegreeFahrenheit` to `HeatTransferCoefficient`. - Regenerated UnitsNet source, resources, number extensions, and generated tests. - Fixed the per-second heat transfer coefficient abbreviations to avoid invalid hour-based aliases. ## Validation - Ran focused tests for `HeatTransferCoefficientTests`, `PowerDensityTests`, and `ThermalConductivityTests` on `net9.0`: 1600 passed. --------- Co-authored-by: Hayley Easter <hayley.easter@ansys.com> Co-authored-by: Andreas Gullberg Larsen <andreas.larsen84@gmail.com>
Adds square millimeter variants for `HeatFlux`, including metric prefixes generated from `WattPerSquareMillimeter`. ## Changes - Added `WattPerSquareMillimeter` to `HeatFlux`. - Added generated metric-prefixed square millimeter units, such as `MilliwattPerSquareMillimeter`, `MicrowattPerSquareMillimeter`, and `KilowattPerSquareMillimeter`. - Regenerated UnitsNet source, resources, number extensions, and generated tests. - Merged current `master` and resolved stale nanoFramework generated-file conflicts by keeping the upstream removal of nanoFramework support. ## Validation - Ran focused `HeatFluxTests` on `net9.0`: 463 passed. - Ran `git diff --check`: passed. Co-authored-by: June Meservy <meservy@nanotest.eu> Co-authored-by: Andreas Gullberg Larsen <andreas.larsen84@gmail.com>
## Summary Adds `UnitInfo` convenience overloads to `UnitAbbreviationsCache` for the same method groups that already accept `UnitKey`: - `GetDefaultAbbreviation(UnitInfo, ...)` - `GetUnitAbbreviations(UnitInfo, ...)` - `MapUnitToAbbreviation(UnitInfo, ...)` - `MapUnitToDefaultAbbreviation(UnitInfo, ...)` This avoids caller-side `unitInfo.UnitKey` extraction when the caller already has a `UnitInfo`, while keeping the existing `UnitKey`, typed enum, and `(Type, int)` overloads. ## Maintainer Updates - Added explicit `ArgumentNullException(nameof(unitInfo))` handling for the new public overloads. - Kept the overloads scoped to the cache's configured quantities by resolving the supplied `UnitInfo.UnitKey` through the cache's `QuantityInfoLookup` before reading or mapping abbreviations. - Added tests for null `UnitInfo` inputs. - Added tests confirming a cache constructed with a limited quantity set rejects `UnitInfo` values outside that set. ## Validation - Ran `dotnet test UnitsNet.Tests --filter "FullyQualifiedName~UnitAbbreviationsCacheTests" --no-restore --verbosity minimal`. - Result: 36 tests passed per framework across net8, net9, and net10. --------- Co-authored-by: Andreas Gullberg Larsen <andreas.larsen84@gmail.com>
## Motivation Issue #1663 reports that the generated abbreviations for MolarFlow per-hour units are shifted by one kilo prefix: - `MolarFlowUnit.MolePerHour` formats/parses as `kmol/h` - `MolarFlowUnit.KilomolePerHour` formats/parses as `kkmol/h` The root cause is that `MolePerHour` has `Prefixes: [ "Kilo" ]`, but its base abbreviation in `MolarFlow.json` was already written as `kmol/h`. The prefix generator then prepended another `k` for the generated `KilomolePerHour` unit. ## Changes - Change `MolePerHour` abbreviation from `kmol/h` to `mol/h`. - Regenerate MolarFlow resources and generated tests. - Generated abbreviations now match the other molar-flow units: - `MolePerHour`: `mol/h` - `KilomolePerHour`: `kmol/h` ## Validation - `generate-code.bat` - `dotnet test UnitsNet.Tests --filter "FullyQualifiedName~MolarFlowTests"` Fixes #1663.
## Why `UnitsNetBaseJsonConverter` instantiates registered custom quantity types with `Activator.CreateInstance()`. If that reflection call fails, or if it returns `null`, the converter should not return `null` from its non-null `IQuantity` conversion path or surface an unclear reflection exception. ## What - Throw a `UnitsNetException` when a registered custom quantity cannot be instantiated. - Include the quantity type, unit type, unit value, and a stable error code in `Exception.Data`. - Use `UnitsNetException.ErrorCodeDataKey` as the shared `Exception.Data` key for error codes. - Preserve the original reflection/constructor failure as the inner exception. - Add regression tests for failed registered quantity instantiation and constructor failures. ## Validation - `dotnet test UnitsNet.Serialization.JsonNet.Tests -f net8.0 --filter "FullyQualifiedName~UnitsNetBaseJsonConverterTest"` - `dotnet test UnitsNet.Serialization.JsonNet.Tests -f net9.0 --filter "FullyQualifiedName~UnitsNetBaseJsonConverterTest"` - `dotnet test UnitsNet.Serialization.JsonNet.Tests -f net10.0 --filter "FullyQualifiedName~UnitsNetBaseJsonConverterTest"` --------- Co-authored-by: June Meservy <meservy@nanotest.eu> Co-authored-by: Andreas Gullberg Larsen <andreas.larsen84@gmail.com>
## Motivation Some generated unit relations that are useful in structural engineering were missing. Adding them makes it possible to express common derived relationships directly with UnitsNet quantities instead of manually converting through raw numeric values. ## Changes - Add relation definitions for structural-engineering workflows, including density/length to area density, area/area to area moment of inertia, specific weight/volume to force, and acceleration/area density to pressure. - Regenerate the affected relation operators and tests. ## Validation - GitHub PR Build: passing. - Azure UnitsNet PR: passing. - Local maintainer check: merged with current `master`, reran code generation with no extra diff, and ran the affected relation test classes on net9 successfully. - The only red check is Claude code review, which should not block this PR.
## Motivation
The current quantity interface hierarchy duplicates generic-math
contracts and couples arithmetic
capabilities to interfaces that also carry the unit enum type.
This results in verbose generic constraints such as:
```csharp
where TQuantity : ILinearQuantity<TQuantity>, IQuantity<TQuantity, TUnit>
```
Linear and logarithmic quantities also declare many of the same
arithmetic operator interfaces
independently, making the hierarchy harder to understand and extend.
This PR separates the arithmetic capability from unit typing and
composes the more specific quantity
interfaces from those reusable contracts.
## Changes
- Introduce `IArithmeticQuantity<TSelf>` as the unit-independent
arithmetic capability.
- Make `IArithmeticQuantity<TSelf, TUnit>` combine:
- `IQuantity<TSelf, TUnit>`
- `IArithmeticQuantity<TSelf>`
- Introduce `ILinearQuantity<TSelf, TUnit>` as the strongly typed linear
quantity contract.
- Reuse `IArithmeticQuantity<TSelf>` from both linear and logarithmic
quantities.
- Move affine addition/subtraction contracts to the unit-independent
`IAffineQuantity<TSelf, TOffset>` interface.
- Generate linear quantities as `ILinearQuantity<TSelf, TUnit>`.
- Simplify `Sum()` and `Average()` constraints to:
```csharp
where TQuantity : ILinearQuantity<TQuantity, TUnit>
```
- Regenerate affected quantity declarations.
## Result
Generic APIs can express the intended quantity capability with one
constraint:
```csharp
static TQuantity Sum<TQuantity, TUnit>(
IEnumerable<TQuantity> quantities,
TUnit unit)
where TQuantity : ILinearQuantity<TQuantity, TUnit>
where TUnit : struct, Enum
{
return quantities.Sum(unit);
}
```
The hierarchy also gives linear and logarithmic quantities a common
arithmetic capability without
requiring a unit enum when the generic algorithm does not use units.
This is intended as an interface-organization change, not a behavior
change. Existing arithmetic
interfaces remain transitively implemented by the generated quantity
types.
## Compatibility considerations
This changes the declared interface hierarchy of generated quantity
structs, so the public API and
binary compatibility implications should be reviewed carefully even
though assignability through
the existing base interfaces is preserved.
---------
Co-authored-by: Andreas Gullberg Larsen <andreas.larsen84@gmail.com>
> [!NOTE]
> While v6 is still prerelease, we may instead move the complete
`ToUnit` API—including the base
> member and typed overloads—to extension methods. Moving them together
avoids member shadowing and
> further reduces the burden on custom quantities.
## Motivation
`IQuantity<TSelf, TUnitType>` preserves the concrete quantity type for
static factories and generic
quantity metadata, but inherited `ToUnit(TUnitType)` currently returns
the weaker
`IQuantity<TUnitType>` contract. Generic callers therefore lose `TSelf`
and must cast or reconstruct
the concrete quantity after a unit conversion.
For example, this cannot currently return `TQuantity` without a cast:
```csharp
static TQuantity ConvertToUnit<TQuantity, TUnit>(TQuantity quantity, TUnit unit)
where TQuantity : IQuantity<TQuantity, TUnit>
where TUnit : struct, Enum
{
return quantity.ToUnit(unit);
}
```
## Changes
- Add a default, self-typed `ToUnit(TUnitType)` member to
`IQuantity<TSelf, TUnitType>` on modern .NET
targets.
- Implement it through the existing `TSelf.From()` and `As()` contracts.
- Delegate the inherited `IQuantity<TUnitType>.ToUnit()` implementation
to the strongly typed member.
- Add a generic test demonstrating that conversion returns `Length`
directly without boxing or an
explicit cast.
The default implementation means existing third-party quantity
implementations do not need to add a
new member. The `netstandard2.0` contract is unchanged, following the
interface's existing
modern-target pattern for static abstract members.
## Validation
- `dotnet build UnitsNet/UnitsNet.csproj --no-restore --verbosity
minimal`
- Builds `netstandard2.0`, `net8.0`, `net9.0`, and `net10.0`.
- `dotnet test UnitsNet.Tests/UnitsNet.Tests.csproj --no-build
--verbosity minimal`
- 42,703 passed and 20 existing skips on each of .NET 8, 9, and 10.
Add new quantity for area per unit length (m²/m), commonly used in structural engineering for distributed reinforcement. Includes metric (cm²/m, mm²/m) and imperial (in²/ft, in²/in, ft²/ft) units with verified conversion factors. In structural engineering, reinforcement is commonly specified as an area distributed over a length — for example, mm²/m or in²/ft. This is the standard way to express required steel reinforcement in concrete structures. My specific application is designing cylindrical prestressed concrete tanks (per AWWA D110 / ACI 372), where reinforcement requirements are calculated as area of steel per unit height or per unit circumference (e.g., in²/ft of tank wall). --------- Co-authored-by: Andreas Gullberg Larsen <andreas.larsen84@gmail.com>
Motivation: .NET Framework cannot use generic Enum.GetValues<T>(), preventing CLR4 test compilation. Changes: Use EnumHelper in generated and custom enum tests.
Motivation: Exception messages differ between CLR implementations. Changes: Assert ArgumentNullException.ParamName instead of message text.
Motivation: The build scripts assumed Windows paths and executable names. Changes: Use platform-neutral paths and tools, and build net48 benchmarks only on Windows.
Motivation: The Math.Pow fallback returns NaN for negative values even when an odd root is real. Changes: Move the fallback to reusable MathHelper code, use it on netstandard, and test it directly.
Motivation: Modern static interface members are unavailable on CLR4, and the custom fixture missed its netstandard contract. Changes: Guard modern-only tests and implement the required HowMuch instance metadata property.
## Motivation The version bump scripts only updated a subset of the packages they release. In particular, `UnitsNet.NumberExtensions.CS14` and `UnitsNet.Serialization.SystemTextJson` could be left on the previous version. UnitsNet.Modular also had no equivalent bump command for its MinVer tag stream. ## Changes - Update the UnitsNet script to version UnitsNet and both NumberExtensions packages. - Update the JSON script to version both serialization packages and preserve unrelated working changes. - Add a MinVer-aware UnitsNet.Modular script for minor, patch, and prerelease suffix tags. - Document the Modular release command and update the wrapper descriptions. ## Validation - Parsed the PowerShell scripts in Windows PowerShell and PowerShell 7. - Checked the shell wrappers with Git Bash. - Ran both explicit-version scripts in an isolated clone and verified all five project versions, commits, and annotated tags. - Verified the Modular tag flow and MinVer version resolution in an isolated clone. - Ran `git diff --check`.
## Motivation Fixes #1654. Several quantity combinations are already representable in UnitsNet, but were missing generated quantity-derivation operators. This means consumers had to drop down to numeric values or write the relation manually even though both sides are typed quantities. ## Changes - Add the missing relations from #1654 to `UnitRelations.json` and regenerate the quantity code. - Add relation tests for multiplication and inverse division where those operators are generated. - Use `NoInferredDivision` for `VolumePerLength * Length` and `VolumeFlowPerArea * Area`, since inferred `Volume / Length` and `VolumeFlow / Area` divisions would be ambiguous with existing relations. ## Validation - `generate-code.bat` - `dotnet build UnitsNet.Tests\UnitsNet.Tests.csproj --no-restore` - `dotnet test UnitsNet.Tests\UnitsNet.Tests.csproj --no-build --filter "FullyQualifiedName~ElectricPotentialChangeRateTests|FullyQualifiedName~ForceTests|FullyQualifiedName~RotationalAccelerationTests|FullyQualifiedName~IrradianceTests|FullyQualifiedName~ElectricCurrentDensityTests|FullyQualifiedName~ElectricSurfaceChargeDensityTests|FullyQualifiedName~ElectricChargeDensityTests|FullyQualifiedName~HeatTransferCoefficientTests|FullyQualifiedName~ThermalResistanceTests|FullyQualifiedName~ThermalInsulanceTests|FullyQualifiedName~LinearPowerDensityTests|FullyQualifiedName~PowerDensityTests|FullyQualifiedName~VolumePerLengthTests|FullyQualifiedName~VolumeFlowPerAreaTests|FullyQualifiedName~RatioChangeRateTests|FullyQualifiedName~CompressibilityTests|FullyQualifiedName~VolumetricHeatCapacityTests|FullyQualifiedName~LuminousIntensityTests|FullyQualifiedName~MagneticFieldTests|FullyQualifiedName~ElectricCapacitanceTests|FullyQualifiedName~ElectricFieldTests|FullyQualifiedName~ElectricPotentialTests|FullyQualifiedName~FluidResistanceTests|FullyQualifiedName~PressureTests|FullyQualifiedName~AbsorbedDoseOfIonizingRadiationTests|FullyQualifiedName~ElectricApparentPowerTests|FullyQualifiedName~ElectricReactivePowerTests"`
## Motivation PR #1544 left the samples with a development-only package override for normal builds, while only the `Official` configuration used the published NuGet package. That override also pointed to the accidental `pre103` version. The samples should always exercise the package users install. ## Changes - Reference `UnitsNet 6.0.0-pre021` centrally for all samples. - Remove the configuration-specific package overrides. - Update the custom quantity sample to implement `ILinearQuantity`, matching the current v6 aggregation APIs. ## Validation - Restored the sample solution with an empty-cache package resolution. - Built `Samples/Samples.slnx` in Release successfully. - Verified that no development `VersionOverride` or conditional UnitsNet package references remain. - Ran `git diff --check`.
## Motivation
The build scripts should keep the actual test project list easy to see
and avoid project discovery or MSBuild evaluation on every build. The
net48 compatibility workflow runs the same main test projects on .NET
Framework, so it should not duplicate the project names or import the
full build-functions module just to get the list.
## Changes
- move the hard-coded main test project list to
`Build/test-projects.psm1`
- expose the list through `Get-TestProjectPaths`
- call `Get-TestProjectPaths` explicitly from
`Build/build-functions.psm1` and the net48 compatibility workflow
- merge the branch onto latest `master`
## Validation
- `powershell -NoProfile -ExecutionPolicy Bypass -Command 'Import-Module
./Build/test-projects.psm1 -Force; @(Get-TestProjectPaths) |
ForEach-Object { $_ }'`
- `pwsh -NoProfile -Command 'Import-Module ./Build/test-projects.psm1
-Force; @(Get-TestProjectPaths) | ForEach-Object { $_ }'`
- `powershell -NoProfile -ExecutionPolicy Bypass -Command 'Import-Module
./Build/build-functions.psm1 -Force'`
- `pwsh -NoProfile -Command 'Import-Module ./Build/build-functions.psm1
-Force'`
- `git diff --check -- Build/test-projects.psm1
Build/build-functions.psm1 .github/workflows/net48-compatibility.yml`
## Motivation Generated quantities duplicated descriptive metadata across the quantity type, value instances, and registry descriptors. The prototype also introduced a separate `UnitsNet.Core` project as a possible contract boundary with legacy UnitsNet, but the two models currently have deliberately different contracts and Core has no independent consumer or versioning boundary. This PR makes static `Quantity.Info` the canonical metadata object and keeps the clean quantity contracts and runtime in the consumer-facing `UnitsNet.Modular` package. It supersedes #1713 without introducing a second package that would immediately need to be removed. ## Changes - make each generated quantity's static `Info` object the canonical home for identity, base-unit metadata, unit metadata, and base dimensions - standardize on `QuantityInfo.BaseUnit`, `QuantityInfo.Units`, and `UnitInfo.Value` - reuse the same `Info` instance through the generated discovery registry instead of constructing a parallel descriptor graph - define slim self-typed quantity and arithmetic capability contracts with reusable conversion and aggregation behavior - move the contracts, immutable metadata, conversion helpers, and quantity math into the `UnitsNet.Modular` project and namespace - remove the standalone `UnitsNet.Core` project, package, dependency, and two-package release plumbing - preserve strong naming and the stable `6.0.0.0` assembly version on the unified runtime - hide source-compatibility aliases and generator-infrastructure APIs from IntelliSense - update generated code, samples, migration guidance, architecture documentation, CI, and local package automation for the single-package model ## Consumer impact Consumers install only `UnitsNet.Modular`. The package contains the runtime, contracts, metadata types, and bundled source generator. This intentionally does not preserve the unpublished alpha `UnitsNet.Core.*` or earlier Modular metadata surface. Ordinary strongly typed construction, conversion, parsing, formatting, arithmetic, and unit APIs remain source compatible where the selected catalog contains the required quantities and units. The main `UnitsNet` project and its existing interfaces remain unchanged. Extracting a shared contracts package is deferred until a separate legacy/modular investigation demonstrates a genuinely useful common boundary. ## Validation - `dotnet restore UnitsNet.Modular/UnitsNet.Modular.slnx --force-evaluate -p:UnitsNetModularSampleUpdateLocalPackagesOnBuild=true` - `dotnet build UnitsNet.Modular/UnitsNet.Modular.slnx --no-restore -m:1 -p:UnitsNetModularSampleUpdateLocalPackagesOnBuild=false` - `dotnet test UnitsNet.Modular/UnitsNet.Modular.slnx --no-build -m:1 -p:UnitsNetModularSampleUpdateLocalPackagesOnBuild=false` - 43 compatibility tests passed - 31 generator tests passed - 37 Modular runtime/generated quantity tests passed - the Codespaces playground and all sample projects build - packed `UnitsNet.Modular` successfully for .NET 8, 9, and 10 and verified that its nuspec has no `UnitsNet.Core` dependency
## Motivation Make the first UnitsNet.Modular experience discoverable from IntelliSense, compiler diagnostics, documentation, and a package-based sample that behaves like a real consumer. ## Changes - expand the authoring API XML documentation with complete module, unit-set, custom-spec, and profile examples - link every `UNM` diagnostic to the relevant README section and verify that coverage in a generator test - replace the quick start with two copy-pasteable files, explain generated ownership and relationships, and add troubleshooting guidance plus a scenario-oriented sample index - add an isolated NuGet getting-started sample that exactly matches the documented quick start - run both package-facing consumer scenarios in the separate UnitsNet.Modular workflow ## Validation - `dotnet test UnitsNet.Modular/UnitsNet.Modular.slnx --no-restore -m:1 -p:UnitsNetModularSampleUpdateLocalPackagesOnBuild=false` (115 tests passed; runtime targets net8.0, net9.0, and net10.0) - `pwsh UnitsNet.Modular/Samples/UnitsNet.Modular.GettingStarted.Sample/run.ps1` - verified generated XML documentation contains the examples and links - verified all diagnostic anchors exist and all relative Markdown links resolve
## Motivation
UnitsNet.Modular forwards quantity format strings such as `F1` to the
stored numeric value, but its public APIs do not currently identify
those parameters as numeric format strings to IDE tooling.
## Changes
- annotate generated quantity `ToString(string)` overloads with
`StringSyntaxAttribute.NumericFormat`
- annotate the type-erased `IQuantityDescriptor.Format` format parameter
- verify the annotations are present in emitted consumer-visible
metadata
This improves validation and completion for direct format-string
arguments where supported by the IDE. Interpolation clauses such as
`$"{speed:F1}"` continue to work, but their completion behavior remains
controlled by the IDE's interpolated-string support.
## Validation
- `dotnet test UnitsNet.Modular/UnitsNet.Modular.slnx --no-restore -m:1
-p:UnitsNetModularSampleUpdateLocalPackagesOnBuild=false`
- 115 tests passed
- Modular runtime built for .NET 8, .NET 9, and .NET 10
## Motivation #1313 reported that custom conversion functions could not override built-in same-quantity conversions in v5. In v6, the supported model is to customize the quantity unit definitions before the converter is built, instead of adding runtime override precedence for built-in conversions. ## Changes - Document how to override built-in unit conversion factors through `UnitsNetSetup.ConfigureDefaults()` or an isolated custom `UnitConverter`. - Add regression coverage for the original `PressureUnit.InchOfWaterColumn` scenario, verifying that `UnitConverter`, `As(..., converter)`, and `ToUnit(..., converter)` all use the configured conversion factor. ## Validation - `dotnet test UnitsNet.Tests\UnitsNet.Tests.csproj --filter "FullyQualifiedName~UnitConverterTest.ConvertValue_WithCustomBuiltInUnitDefinition_UsesConfiguredConversion|FullyQualifiedName~UnitConverterTest.GetConversionFunction_WithCustomUnitConversion|FullyQualifiedName~UnitConverterTest.TryGetConversionFunction_WithCustomUnitConversion"`
## Motivation CodeGen currently relies on default file encodings from `File.WriteAllText`, `File.CreateText`, `StreamWriter`, and related read helpers. Those defaults can differ by runtime/API history and make generated output encoding/BOM behavior less obvious than it should be. We want generated output to be stable across platforms and avoid review noise from UTF-8 BOM differences. ## Changes - Add a small `CodeGenFile` helper that reads text as UTF-8 and detects an existing BOM, but writes generated/codegen-normalized files as explicit UTF-8 without BOM. - Use the helper for generated C# files, generated resource text files, normalized relation JSON, unit enum allocation JSON, quantity JSON reads, and codegen edit helpers. ## Validation - `dotnet build CodeGen\CodeGen.csproj` - `generate-code.bat` - Scanned generated/codegen-normalized outputs and found no UTF-8 BOM. - Running code generation after the change produced no generated-file diffs.
## Summary - Set the repository-wide EditorConfig charset to UTF-8 without BOM. - Remove the existing UTF-8 BOM from all 536 tracked files that contained one. - Keep file contents and line endings otherwise unchanged. ## Motivation UTF-8 has no byte-order ambiguity, so its BOM only serves as an encoding signature. Unicode permits but does not require it, and W3C recommends avoiding it unless a compatibility requirement exists because it can interfere with tools or formats that expect content at byte zero. This also aligns authored files with the explicit UTF-8-without-BOM behavior recently adopted by CodeGen and prevents future encoding-only review noise. References: - https://www.unicode.org/versions/Unicode17.0.0/core-spec/chapter-2/ - https://www.w3.org/International/questions/qa-byte-order-mark.en.php ## Impact This is an encoding-only normalization. No source, project, resource, documentation, or unit-definition content changes. ## Validation - Verified all 537 changed-file diffs: 536 are BOM-only removals and one is the EditorConfig policy change. - Scanned all tracked files and found zero remaining UTF-8 BOMs. - `generate-code.bat` - `dotnet restore UnitsNet.slnx` - `dotnet build UnitsNet.slnx --no-restore` — succeeded with 18 existing obsolete-API warnings and 0 errors. - `git diff --check`
## Summary - reduce the UnitsNet.Modular sample portfolio to six purpose-focused scenarios with concise names - replace `ConsumerOwned` with a documented, solution-grouped `SharedUnitsLibrarySample` - let every sample switch between `ProjectReferences`, `LocalPackages`, and `PublishedPackages` through the IDE solution-platform selector - move the runnable compatibility pair into test fixtures and update documentation, CI, VS Code, and devcontainer paths ## Motivation The previous samples mixed dependency sources into separate projects, used long or ambiguous names, and contained overlapping scenarios. This made it difficult to tell why each sample existed or to compare the same sample against source, locally packed, and published dependencies. The new solution platforms keep `Debug` and `Release` as build configurations while treating the dependency source as the IDE-selectable platform. Local package preparation runs once at solution level to avoid parallel pack races. ## Impact In Rider or Visual Studio, contributors can select combinations such as `Debug | LocalPackages` for the complete sample portfolio. Direct sample builds continue to default to project references. Outputs and restore state are isolated by dependency platform. ## Validation - `dotnet build UnitsNet.Modular.slnx --configuration Release -p:Platform=ProjectReferences` - `dotnet build UnitsNet.Modular.slnx --configuration Release -p:Platform=LocalPackages` - `dotnet build UnitsNet.Modular.slnx --configuration Release -p:Platform=PublishedPackages` - `dotnet test UnitsNet.Modular.slnx --configuration Release -p:Platform=ProjectReferences` — 116 tests passed - local-package custom and shared-units samples executed successfully - published-package getting-started sample executed successfully - `git diff --check`
## Motivation UnitsNet v6 has already removed the proprietary `U`, `V`, and `Q` quantity format strings. This completes that cleanup by removing the remaining `A` and `S` formats, so quantity format parameters consistently accept .NET numeric format strings and can be described accurately with `StringSyntaxAttribute.NumericFormat`. The historical rationale supports the cleanup: - `A` only returns unit abbreviations, which are already available explicitly through generated `GetAbbreviation()` APIs and the configurable `UnitAbbreviationsCache`. - #797 intended to remove `S`, and commit 721287d removed its documentation and tests, but the formatter implementation remained in v5. This PR completes that cleanup. The discussion in #1450 also noted that `S` can hide precision and that callers should choose an explicit numeric format. ## Changes - Remove `A`/`An` and `S`/`Sn` formatting from `QuantityFormatter` and `QuantityValue`. - Throw focused `FormatException` messages for those formerly recognized formats with migration guidance. - Keep standard and custom .NET numeric formats, such as `G3`, `F2`, `N2`, `E2`, and `0.##`. - Annotate generated quantity formatting, `QuantityFormatter`, `QuantityValue`, quantity extensions, and type-converter formatting with `StringSyntaxAttribute.NumericFormat`. - Add an internal `StringSyntaxAttribute` compatibility definition for the `netstandard2.0` target. - Regenerate quantities and tests, and update the string-formatting guide and v6 upgrade guide. ## Migration | Removed | Replacement | |---|---| | `A`, `A0`, `A1`, ... | `Length.GetAbbreviation(unit)` or `UnitAbbreviationsCache.GetUnitAbbreviations(unit)` | | `S`, `S2`, ... | An explicit standard/custom numeric format such as `G3`, `F2`, `N2`, `E2`, or `0.##` | | `U` | `quantity.Unit` | | `V` | `quantity.Value` | | `Q` | Static metadata such as `Length.Info.Name` | Currency (`C`) and percent (`P`) formats remain intentionally unsupported for physical quantities. Refs #1200. ## Validation - `generate-code.bat` - `dotnet build UnitsNet/UnitsNet.csproj --no-restore` (`netstandard2.0`, `net8.0`, `net9.0`, `net10.0`) - `dotnet test UnitsNet.Tests --no-restore -f net10.0` (52,506 passed, 16 skipped) - `dotnet test UnitsNet.Tests --no-restore -f net48` (45,321 passed, 16 skipped)
## Summary Add three `VolumeFlow` units commonly used in machining and material-removal workflows: 1. Cubic inches per second (`in³/s`) 2. Cubic inches per minute (`in³/min`) 3. Cubic millimeters per minute (`mm³/min`) The generated API, enum values, localization resources, numeric extensions, and conversion tests have been updated. ## Motivation In subtractive manufacturing (like milling and lathework) and in additive manufacturing (like 3D printing), material rates are written in these alternate formats. A web search for "material removal rate" or "extrusion rate" will confirm the ubiquity of these unit types in manufacturing. Rates for mm^3/min will be used to describe things like the performance of a drill bit (or similar cutting tool). ## Testing - Ran `build.bat` successfully.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )