![]() |
VOOZH | about |
dotnet add package ApprenticeFoundryRulesAndUnits --version 10.10.0
NuGet\Install-Package ApprenticeFoundryRulesAndUnits -Version 10.10.0
<PackageReference Include="ApprenticeFoundryRulesAndUnits" Version="10.10.0" />
<PackageVersion Include="ApprenticeFoundryRulesAndUnits" Version="10.10.0" />Directory.Packages.props
<PackageReference Include="ApprenticeFoundryRulesAndUnits" />Project file
paket add ApprenticeFoundryRulesAndUnits --version 10.10.0
#r "nuget: ApprenticeFoundryRulesAndUnits, 10.10.0"
#:package ApprenticeFoundryRulesAndUnits@10.10.0
#addin nuget:?package=ApprenticeFoundryRulesAndUnits&version=10.10.0Install as a Cake Addin
#tool nuget:?package=ApprenticeFoundryRulesAndUnits&version=10.10.0Install as a Cake Tool
FoundryRulesAndUnits is a comprehensive, modernized unit system library providing type-safe unit conversions, measurement operations, and mathematical operations with automatic type inference. This library supports 6 complete unit systems (SI, MKS, CGS, FPS, IPS, mmNs) with 27 unit families including currency and cost tracking, plus advanced features for engineering and scientific applications.
Current Version: 10.10.0 | Target: .NET 9.0 | Architecture: UnitGroup injection with IUnitSystem interface
ContextWrapper<T>.Error("Not found")ContextWrapper<T>.Ok(data) or ContextWrapper<T>.Ok(list)ContextWrapper<T>.Empty()new ContextWrapper<string>("text") constructorStatusBits property (upgraded from 5-bit StaleBits in v10.6.0)IsOnly*Stale() methods - use natural if-else-if patterns insteadIsDirty for broad usage across all domains (diagrams, evaluators, knowledge systems)ClearAllStaleFlags() to preserve RecomputeBoundary flag (v10.8.1)IsTransformStale: GPU transform cache is stale (C# has fresh position/rotation/scale data)IsMaterialStale: GPU material cache is stale (C# has fresh color/texture/shader data)IsGeometryStale: GPU geometry cache is stale (C# has fresh mesh/vertex data)IsStructureStale: GPU structure cache is stale (C# has fresh hierarchy data)IsDataStale: GPU data cache is stale (C# has fresh custom data)IsNew flag for newly created objectsInvisible, NotDirty) so SetAll(false) = clean state<PackageReference Include="ApprenticeFoundryRulesAndUnits" Version="10.10.0" />
using FoundryRulesAndUnits.Units;
// Create unit system (dependency injection friendly)
var unitSystem = IUnitSystem.MKS(); // or SI(), FPS(), IPS(), CGS()
// Alternative: Constructor approach
var unitSystem = new UnitSystem(UnitSystemType.MKS);
// Use throughout application via dependency injection or direct usage
var unitSystem = IUnitSystem.MKS(); // Create once, use everywhere
// Type-safe creation methods (recommended)
Length length = unitSystem.CreateLength(5.0, "m");
Temperature temp = unitSystem.CreateTemperature(25.0, "ยฐC");
Force force = unitSystem.CreateForce(100.0, "N");
Mass mass = unitSystem.CreateMass(50.0, "kg");
Speed speed = unitSystem.CreateSpeed(60.0, "mph");
// Currency and cost tracking (v10.9.0) โญ NEW!
Currency usd = unitSystem.CreateUnit<Currency>(100.0, "USD");
CostPerQuantity price = unitSystem.CreateUnit<CostPerQuantity>(5.50, "USD/ea");
CostPerTime laborRate = unitSystem.CreateUnit<CostPerTime>(85.00, "USD/hr");
// Generic creation (for parsers)
MeasuredValue parsed = unitSystem.CreateMeasuredValue(UnitFamilyName.Length, 5.0, "m");
var unitSystem = IUnitSystem.MKS();
var length = unitSystem.CreateLength(5.0, "m");
double feet = length.As("ft"); // Convert to feet
double inches = length.As("in"); // Convert to inches
string display = length.AsString("cm"); // "500 cm"
// Rack units for data center applications (v10.10.0) โญ NEW!
var serverHeight = unitSystem.CreateLength(2, "RU"); // 2U server
double heightInches = serverHeight.As("in"); // 3.5 inches
double heightMM = serverHeight.As("mm"); // 88.9 mm
// Direct system conversion
double converted = unitSystem.Convert(100, "cm", "in"); // 39.37 inches
var unitSystem = IUnitSystem.MKS();
var length1 = unitSystem.CreateLength(10, "m");
var length2 = unitSystem.CreateLength(5, "ft");
var total = length1 + length2; // Addition (same family)
var difference = length1 - length2; // Subtraction
var scaled = length1 * 2.0; // Scalar multiplication
var ratio = length1 / length2; // Ratio (returns double)
var unitSystem = IUnitSystem.MKS();
// Cross-family operations with automatic type inference
Length width = unitSystem.CreateLength(5, "m");
Length height = unitSystem.CreateLength(3, "m");
var area = width * height; // Returns Area automatically!
Mass mass = unitSystem.CreateMass(100, "kg");
var acceleration = unitSystem.CreateAcceleration(9.8, "m/s2");
var force = mass * acceleration; // Returns Force automatically!
// Compare measurements
if (length1 > length2) {
Console.WriteLine("Length1 is longer");
}
var unitSystem = IUnitSystem.MKS();
var currencyGroup = unitSystem.GetUnitGroup(UnitFamilyName.Currency);
// Multi-currency support with 14 international currencies
var usd = unitSystem.CreateUnit<Currency>(100, "USD");
var eur = unitSystem.CreateUnit<Currency>(92, "EUR");
var gbp = unitSystem.CreateUnit<Currency>(78, "GBP");
// Convert between currencies (hub-and-spoke via USD)
var usdValue = eur.As("USD"); // Returns 100.00
Console.WriteLine(usd.FormatCurrency()); // "$100.00"
Console.WriteLine(eur.FormatCurrency()); // "โฌ92.00"
// Cost per quantity - discrete item pricing
var screwPrice = unitSystem.CreateUnit<CostPerQuantity>(0.15, "USD/ea");
var totalCost = screwPrice.CalculateCost(250, currencyGroup);
Console.WriteLine($"250 screws: {totalCost.FormatCurrency()}"); // "$37.50"
// Volume discounts
var bulkPrice = screwPrice.ApplyVolumeDiscount(15); // 15% off
Console.WriteLine($"Bulk: {bulkPrice.FormatCost()}"); // "$0.13/ea"
// Cost per time - labor and service rates
var laborRate = unitSystem.CreateUnit<CostPerTime>(75.00, "USD/hr");
var laborCost = laborRate.CalculateCost(40, currencyGroup); // 40 hours
Console.WriteLine($"Weekly labor: {laborCost.FormatCurrency()}"); // "$3,000.00"
// Rate adjustments (markup/discount)
var contractRate = laborRate.ApplyAdjustment(25); // 25% markup
Console.WriteLine($"Contract: {contractRate.FormatCost()}"); // "$93.75/hr"
// Supported currencies: USD, EUR, GBP, JPY, CNY, CAD, AUD, CHF, INR, MXN, BRL, KRW, SGD, HKD, cent
var unitSystem = IUnitSystem.MKS();
// Parser-accessible families (primary - used by parsers)
Length length = unitSystem.CreateLength(5.0, "ft"); // Length family
Angle angle = unitSystem.CreateAngle(45.0, "deg"); // Angle family
Duration duration = unitSystem.CreateDuration(30, "s"); // Duration family
// Function-only families (secondary - via AS functions)
// These prevent parser ambiguity but provide specialized functionality
Distance distance = unitSystem.CreateDistance(5.0, "ft"); // Distance family
Bearing bearing = unitSystem.CreateBearing(45.0, "deg"); // Bearing family
Time time = unitSystem.CreateTime(30, "s"); // Time family
// Key insight: Parser sees "ft" โ Length, "deg" โ Angle, "s" โ Duration
// No ambiguity! Distance/Bearing/Time accessed via specific creation methods
Two-Tier Benefits:
Available Two-Tier Families:
ALL unit classes follow the modern UnitGroup injection pattern:
โ Physical & Mechanical Units:
Length, Mass, Temperature, Volume, ForceSpeed, Power, Area, Duration, DistanceFrequency, Timeโ Electrical Units:
Voltage, Resistance, Current, Capacitanceโ Digital & Computing Units:
DataStorage, DataFlowโ Financial Units (v10.9.0): โญ NEW!
Currency, CostPerQuantity, CostPerTimeโ Specialized Units:
Quantity, QuantityFlow, Percent, DimensionlessAngle, Bearing๐ ALL CLASSES FEATURE:
// Create unit systems (dependency injection friendly)
var mksSystem = IUnitSystem.MKS();
var siSystem = IUnitSystem.SI();
var fpsSystem = IUnitSystem.FPS();
// Switch unit systems dynamically
var system = IUnitSystem.MKS();
system.Apply(UnitSystemType.FPS); // Switch to Imperial
var converted = system.Convert(100, "m", "ft"); // 328.084 feet
// Validation and metadata
bool isValid = system.IsValidUnit("mph"); // true
var units = system.GetUnitsForFamily(UnitFamilyName.Length); // ["m", "cm", "km", ...]
string baseUnit = system.GetBaseUnitForFamily(UnitFamilyName.Mass); // "kg" (MKS)
The modern architecture supports clean dependency injection:
// Service registration (e.g., in Program.cs or Startup.cs)
services.AddSingleton<IUnitSystem>(_ => IUnitSystem.MKS());
// Usage in classes
public class Calculator
{
private readonly IUnitSystem _unitSystem;
public Calculator(IUnitSystem unitSystem)
{
_unitSystem = unitSystem;
}
public Force CalculateForce(double mass, double acceleration)
{
var m = _unitSystem.CreateMass(mass, "kg");
var a = _unitSystem.CreateAcceleration(acceleration, "m/s2");
return m * a; // Returns Force automatically
}
}
The two-tier family system eliminates parser ambiguity:
public class UnitParser
{
private readonly IUnitSystem _unitSystem;
public UnitParser(IUnitSystem unitSystem)
{
_unitSystem = unitSystem;
}
public MeasuredValue Parse(string input)
{
var (value, unit) = ExtractValueAndUnit(input); // "100 cm" โ 100, "cm"
// Creates correct derived type automatically (Length, Angle, Mass, etc.)
// Zero ambiguity: "cm" โ Length, "deg" โ Angle, "s" โ Duration
return _unitSystem.CreateMeasuredValueFromParsableUnit(unit, value);
}
}
ContextWrapper<T> provides a standardized response pattern for service APIs with built-in error handling, timestamps, and payload management.
// Success response
var agents = new List<AgentDTO> { agent1, agent2 };
return new ContextWrapper<AgentDTO>(agents)
{
message = "Retrieved 2 agents"
};
// Error response
return new ContextWrapper<AgentDTO>
{
hasError = true,
message = "Database connection failed"
};
// Non-generic interface for common properties
public interface IContextWrapper
{
bool hasError { get; set; }
string message { get; set; }
int length { get; }
DateTime timestamp { get; }
}
// Generic interface with typed payload
public interface IContextWrapper<out T> : IContextWrapper
{
List<T> payload { get; }
}
// Implementation
public class ContextWrapper<T> : IContextWrapper<T>
{
// Existing implementation
}
using FoundryRulesAndUnits.Extensions;
// Error propagation across different types
var agentResult = await GetAgentAsync(id);
if (agentResult.hasError)
return agentResult.AsErrorFor<DocumentDTO>(); // Type conversion
// Convenience checks
if (result.IsEmpty()) // hasError || length == 0
return ContextWrapper<T>.Error("Not found");
if (result.HasData()) // !hasError && length > 0
ProcessData(result.payload);
// Quick error creation (multiple syntaxes)
return ContextWrapper<AgentDTO>.Error("Agent not found");
return new ContextWrapper<AgentDTO>("Agent not found"); // Also valid
// Fluent error setting
return result.SetError("Validation failed");
CRITICAL: The dangerous new ContextWrapper<string>("text") constructor has been REMOVED!
| Method | Use Case | Result |
|---|---|---|
Error("message") |
Something went wrong | hasError = true, no payload |
Ok(item) |
Success with single item | hasError = false, 1 item in payload |
Ok(items) |
Success with collection | hasError = false, multiple items |
Empty() |
Success but no data | hasError = false, empty payload |
// ๐จ REMOVED - This constructor no longer exists (was ambiguous for string payloads)
// new ContextWrapper<string>("text") - โ COMPILE ERROR!
// โ
REQUIRED - Use factory method for errors without payload
var errorWrapper = ContextWrapper<string>.Error("Error message"); // Explicit error
// โ
PREFERRED - Use factory method for clarity
var dataWrapper = ContextWrapper<string>.Ok("Actual string data"); // Explicit payload
// โ
STILL VALID - Constructor with payload object works fine
var wrapper1 = new ContextWrapper<string>("data"); // String is payload (T)
var wrapper2 = new ContextWrapper<DocumentDTO>(document); // Object is payload (T)
var wrapper3 = new ContextWrapper<string>(list); // List of strings
// Works perfectly for all types
public async Task<ContextWrapper<DocumentDTO>> GetDocumentAsync(string id)
{
var result = await _service.FindAsync(id);
if (result.IsEmpty())
return ContextWrapper<DocumentDTO>.Error($"Document {id} not found");
return ContextWrapper<DocumentDTO>.Ok(result);
}
// All factory methods available:
return ContextWrapper<T>.Error("Error message"); // Error with no payload (REQUIRED - no constructor for this)
return ContextWrapper<T>.Ok(singleItem); // Success with one item (or use constructor)
return ContextWrapper<T>.Ok(itemList); // Success with multiple items (or use constructor)
return ContextWrapper<T>.Empty(); // Empty success (or use parameterless constructor)
Why This Matters:
new ContextWrapper<string>("foo") was ambiguous - error or data?Error("foo") or Ok("foo")The static factory methods are the preferred way to create ContextWrapper<T> instances:
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
public async Task<ContextWrapper<User>> GetUser(int id)
{
// Input validation - crystal clear this is an error
if (id <= 0)
return ContextWrapper<User>.Error("Invalid user ID");
// Database lookup
var user = await _userService.GetByIdAsync(id);
if (user == null)
return ContextWrapper<User>.Error($"User {id} not found");
// Success - crystal clear this is data
return ContextWrapper<User>.Ok(user);
}
[HttpGet]
public async Task<ContextWrapper<User>> GetAllUsers()
{
var users = await _userService.GetAllAsync();
// Empty but not an error - crystal clear intent
if (!users.Any())
return ContextWrapper<User>.Empty();
// Success with collection
return ContextWrapper<User>.Ok(users, $"Found {users.Count()} users");
}
}
public class DocumentService
{
public async Task<ContextWrapper<string>> GetContentAsync(string path)
{
// Validation errors
if (string.IsNullOrWhiteSpace(path))
return ContextWrapper<string>.Error("Document path is required");
if (!File.Exists(path))
return ContextWrapper<string>.Error($"File not found: {path}");
try
{
var content = await File.ReadAllTextAsync(path);
// Empty file (success, but no content)
if (string.IsNullOrEmpty(content))
return ContextWrapper<string>.Empty();
// Success with string payload
return ContextWrapper<string>.Ok(content, "Document loaded");
}
catch (Exception ex)
{
return ContextWrapper<string>.Error($"Error reading: {ex.Message}");
}
}
}
Error() vs Ok() vs Empty() - no guessingContextWrapper<T>.Recommendation: Use factory methods for all new code! ๐
public async Task<ContextWrapper<DocumentDTO>> CreateDocumentAsync(
string agentId, string content)
{
// Get agent first
var agentResult = await _agentService.GetByIdAsync(agentId);
// Fail fast - propagate error with type conversion
if (agentResult.hasError)
return agentResult.AsErrorFor<DocumentDTO>();
// Check for empty data
if (agentResult.IsEmpty())
return ContextWrapper<DocumentDTO>.Error($"Agent {agentId} not found"); // Happy path - continue with document creation
var agent = agentResult.payload.First();
var doc = await _documentService.CreateAsync(agent, content);
return doc;
}
// Unit system validation
var unitSystem = IUnitSystem.MKS();
Debug.Assert(unitSystem.IsValidUnit("kg"));
Debug.Assert(unitSystem.Convert(1000, "g", "kg") == 1.0);
// Measurement validation
var length = unitSystem.CreateLength(1.0, "km");
Debug.Assert(Math.Abs(length.As("m") - 1000.0) < 0.001);
// Cross-family operations
var width = unitSystem.CreateLength(5, "m");
var height = unitSystem.CreateLength(3, "m");
var area = width * height; // Should be Area with 15 mยฒ base value
Debug.Assert(area.GetType() == typeof(Area));
FoundryRulesAndUnits/
โโโ Models/
โ โโโ StatusBitArray.cs # โญ v10.8.0: Simplified 32-bit flag system with clean API
โ โโโ ContextWrapper.cs # Generic API response wrapper with IContextWrapper interface
โ โโโ IContextWrapper.cs # Non-generic interface for error handling
โ โโโ [Other data models...]
โโโ UnitSystem/
โ โโโ MeasuredValue.cs # Base class with UnitGroup injection
โ โโโ UnitSystem.cs # Complete IUnitSystem implementation
โ โโโ IUnitSystem.cs # Modern interface with static factory methods
โ โโโ UnitTypeRegistry.cs # Performance-optimized type cache
โ โโโ UnitFamilyName.cs # Enum defining all unit families
โ โโโ UnitTypeAttribute.cs # Attribute for type registration
โ โโโ Specifications/ # Unit system definitions
โ โ โโโ IUnitSystemSpecification.cs
โ โ โโโ SIUnitSystemSpecification.cs # SI unit definitions
โ โ โโโ MKSUnitSystemSpecification.cs # MKS unit definitions
โ โ โโโ [Other system specifications...]
โ โโโ UnitTypes/ # Individual unit classes
โ โโโ Length.cs # โ
UnitGroup injection + UnitTypeAttribute
โ โโโ Mass.cs # โ
Enhanced operators + type safety
โ โโโ Temperature.cs # โ
Cross-family operations
โ โโโ Force.cs # โ
Mathematical operations
โ โโโ [24+ other unit types...]
โโโ Extensions/
โ โโโ BasicMath.cs # Mathematical utilities
โ โโโ JsonUtilities.cs # System.Text.Json support
โ โโโ ContextWrapperExtensions.cs # Helper methods (AsErrorFor, IsEmpty, HasData, etc.)
โ โโโ [Other utility extensions...]
Follow the UnitGroup injection pattern:
[System.Serializable]
[UnitType(UnitFamilyName.MyFamily, Description = "My unit description")]
public class MyUnit : MeasuredValue
{
// UnitFamily comes from UnitTypeAttribute - no need for redundant property override
/// <summary>
/// Constructor with UnitGroup injection - preferred for factory pattern
/// </summary>
public MyUnit(UnitGroup unitGroup) : base(unitGroup)
{
if (unitGroup.Family != UnitFamilyName.MyFamily)
throw new ArgumentException($"UnitGroup family must be {UnitFamilyName.MyFamily}");
}
// Required methods: Assign, Copy, operators
public MyUnit Assign(double value, string? units)
{
Init(value, units);
return this;
}
public MyUnit Copy()
{
var copy = new MyUnit(_unitGroup);
copy.Init(Value(), Internal());
return copy;
}
// Operators using UnitGroup pattern
public static MyUnit operator +(MyUnit left, MyUnit right)
{
var result = new MyUnit(left._unitGroup);
result.Init(left.Value() + right.Value(), left.Internal());
return result;
}
}
// Add creation method to interface
MyUnit CreateMyUnit(double value = 0, string? units = null);
// Implement in UnitSystem class
public MyUnit CreateMyUnit(double value = 0, string? units = null)
{
return CreateUnit<MyUnit>(value, units);
}
MIT License - See LICENSE file for details.
Version: 10.10.0 | Target: .NET 9.0 | Updated: December 2025
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 5 NuGet packages that depend on ApprenticeFoundryRulesAndUnits:
| Package | Downloads |
|---|---|
|
ApprenticeFoundryBlazor
A comprehensive C# / Blazor diagramming library that combines 2D and 3D visualization capabilities. Supports .NET 9, includes advanced layout algorithms, glued connections, multi-page diagrams, and seamless 2D/3D integration. |
|
|
ApprenticeFoundryBlazorThreeJS
3D graphics for blazor applications |
|
|
ApprenticeFoundryMessageLibrary
Package Description |
|
|
ApprenticeFoundryMentorModeler
Package Description |
|
|
ApprenticeFoundryWorldsAndDrawings
Unified 2D and 3D drawing and graphics library for Blazor applications |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.10.0 | 159 | 12/28/2025 |
| 10.9.0 | 231 | 12/21/2025 |
| 10.8.2 | 308 | 12/4/2025 |
| 10.8.1 | 163 | 11/29/2025 |
| 10.7.0 | 216 | 11/23/2025 |
| 10.6.0 | 294 | 11/16/2025 |
| 10.5.0 | 213 | 11/15/2025 |
| 10.4.1 | 362 | 11/12/2025 |
| 10.4.0 | 250 | 11/6/2025 |
| 10.3.0 | 284 | 11/2/2025 |
| 10.2.0 | 232 | 11/2/2025 |
| 10.1.0 | 253 | 10/30/2025 |
| 10.0.0 | 252 | 10/29/2025 |
| 9.3.0 | 169 | 10/25/2025 |
| 9.2.0 | 264 | 10/22/2025 |
| 9.1.0 | 293 | 10/14/2025 |
| 9.0.0 | 365 | 9/23/2025 |
| 8.1.0 | 377 | 9/22/2025 |
| 7.3.0 | 266 | 8/11/2025 |
| 7.2.0 | 355 | 8/10/2025 |