A comprehensive .NET library for creating type-safe, validated string and physics quantity types using semantic meaning. Transform primitive string and numeric obsession into strongly-typed, self-validating domain models with 50+ validation attributes, polymorphic path handling, complete physics system covering 80+ quantities across 8 scientific domains, centralized physical constants with dimensional analysis, and performance-optimized utilities. Features include bootstrap architecture for circular dependency resolution, factory pattern support, dependency injection integration, and enterprise-ready capabilities for building robust, maintainable scientific and domain-specific applications.
$ dotnet add package ktsu.Semantics.QuantitiesA comprehensive .NET library for creating type-safe, validated types with semantic meaning. Transform primitive string and numeric obsession into strongly-typed, self-validating domain models with comprehensive validation, specialized path handling, and a complete physics quantities system covering 80+ quantities across 8 major scientific domains with dimensional analysis and centralized physical constants.
The Semantics library enables you to create strongly-typed wrappers that carry semantic meaning and built-in validation. Instead of passing raw primitives around your application, you can create specific types like EmailAddress, FilePath, Temperature, or UserId that are impossible to misuse and automatically validate their content.
dotnet add package ktsu.Semanticsusing ktsu.Semantics;
// Define strongly-typed domain models
[IsEmail]
public sealed record EmailAddress : SemanticString<EmailAddress> { }
[HasLength(8, 50), IsNotEmpty]
public sealed record UserId : SemanticString<UserId> { }
// Simple direct usage - Clean API with type inference:
// 1. Create methods (recommended) - no generic parameters needed!
var email1 = EmailAddress.Create("user@example.com");
var userId1 = UserId.Create("USER_12345");
// 2. From character arrays
char[] emailChars = ['u', 's', 'e', 'r', '@', 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm'];
var email2 = EmailAddress.Create(emailChars);
// 3. From ReadOnlySpan<char> (performance optimized)
var userId2 = UserId.Create("USER_12345".AsSpan());
// 4. Explicit string casting
var email3 = (EmailAddress)"user@example.com";
var userId3 = (UserId)"USER_12345";
// 5. Safe creation with TryCreate (no exceptions)
if (EmailAddress.TryCreate("maybe@invalid", out EmailAddress? safeEmail))
{
// Use safeEmail - validation succeeded
}
// Compile-time safety prevents mistakes
public void SendWelcomeEmail(EmailAddress to, UserId userId) { /* ... */ }
// This won't compile - type safety in action!
// SendWelcomeEmail(userId, email); // ❌ Compiler error!// Use factory pattern (recommended for dependency injection)
var emailFactory = new SemanticStringFactory<EmailAddress>();
var userFactory = new SemanticStringFactory<UserId>();
// Clean overloaded API - Create methods
var email = emailFactory.Create("user@example.com");
var userId = userFactory.Create("USER_12345");
// All input types supported via overloading
var email2 = emailFactory.Create(['u', 's', 'e', 'r', '@', 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm']);
var userId2 = userFactory.Create("USER_12345".AsSpan());
// Safe creation with TryCreate
if (emailFactory.TryCreate("maybe@invalid", out EmailAddress? safeEmail))
{
// Success!
}
// Legacy FromString methods still available
var email3 = emailFactory.FromString("user@example.com");// Complete physics system with 80+ quantities across 8 domains
public sealed record Temperature<T> : PhysicalQuantity<Temperature<T>, T> where T : struct, INumber<T> { }
public sealed record Force<T> : PhysicalQuantity<Force<T>, T> where T : struct, INumber<T> { }
public sealed record Energy<T> : PhysicalQuantity<Energy<T>, T> where T : struct, INumber<T> { }
// Create quantities with dimensional safety
var temp = Temperature<double>.FromCelsius(25.0); // 298.15 K
var force = Force<double>.FromNewtons(100.0); // 100 N
var distance = Length<double>.FromMeters(5.0); // 5 m
// Physics relationships with compile-time safety
var work = force * distance; // Results in Energy<double>
var power = work / Time<double>.FromSeconds(10.0); // Results in Power<double>
// Type-safe unit conversions
Console.WriteLine(temp.ToFahrenheit()); // 77°F
Console.WriteLine(force.ToPounds()); // 22.48 lbf
// Access physical constants with type safety
var gasConstant = PhysicalConstants.Generic.GasConstant<double>(); // 8.314 J/(mol·K)
var speedOfLight = PhysicalConstants.Generic.SpeedOfLight<float>(); // 299,792,458 m/s
var planckConstant = PhysicalConstants.Generic.PlanckConstant<decimal>(); // Type-safe constant access
// Dimensional analysis prevents errors
// var invalid = force + temp; // ❌ Compiler error!// Use specialized path types
var fileFactory = new SemanticStringFactory<AbsoluteFilePath>();
var configFile = fileFactory.Create(@"C:\app\config.json");
// Rich path operations
Console.WriteLine(configFile.FileName); // config.json
Console.WriteLine(configFile.FileExtension); // .json
Console.WriteLine(configFile.DirectoryPath); // C:\app
Console.WriteLine(configFile.Exists); // True/False
// Polymorphic path collections
List<IPath> allPaths = [
AbsoluteFilePath.FromString<AbsoluteFilePath>(@"C:\data.txt"),
RelativeDirectoryPath.FromString<RelativeDirectoryPath>(@"logs\app"),
FilePath.FromString<FilePath>(@"document.pdf")
];
// Filter by interface type
var filePaths = allPaths.OfType<IFilePath>().ToList();
var absolutePaths = allPaths.OfType<IAbsolutePath>().ToList();// Combine multiple validation rules
[IsNotEmpty, IsEmail, HasLength(5, 100)]
public sealed record BusinessEmail : SemanticString<BusinessEmail> { }
// Use validation strategies for flexible requirements
[ValidateAny] // Either email OR phone is acceptable
[IsEmail, RegexMatch(@"^\+?\d{10,15}$")]
public sealed record ContactInfo : SemanticString<ContactInfo> { }
// First-class type validation
[IsDateTime]
public sealed record ScheduledDate : SemanticString<ScheduledDate> { }
[IsDecimal, IsPositive]
public sealed record Price : SemanticString<Price> { }
[IsGuid]
public sealed record TransactionId : SemanticString<TransactionId> { }[HasLength(3, 20), IsNotEmpty]
public sealed record ProductSku : SemanticString<ProductSku> { }
[IsPositive, IsDecimal]
public sealed record Price : SemanticString<Price> { }
[IsEmail]
public sealed record CustomerEmail : SemanticString<CustomerEmail> { }
public class Order
{
public CustomerEmail CustomerEmail { get; set; }
public ProductSku[] Items { get; set; }
public Price TotalAmount { get; set; }
}[IsAbsolutePath, DoesExist]
public sealed record ConfigFilePath : SemanticString<ConfigFilePath> { }
[IsIpAddress]
public sealed record ServerAddress : SemanticString<ServerAddress> { }
[IsInRange(1, 65535)]
public sealed record Port : SemanticQuantity<Port, int> { }All physical constants are centralized in PhysicalConstants with type-safe generic access:
// Fundamental constants (SI 2019 definitions)
var c = PhysicalConstants.Generic.SpeedOfLight<double>(); // 299,792,458 m/s
var h = PhysicalConstants.Generic.PlanckConstant<double>(); // 6.62607015×10⁻³⁴ J⋅s
var k = PhysicalConstants.Generic.BoltzmannConstant<double>(); // 1.380649×10⁻²³ J/K
var NA = PhysicalConstants.Generic.AvogadroNumber<double>(); // 6.02214076×10²³ /mol
// Temperature constants
var T0 = PhysicalConstants.Generic.StandardTemperature<double>(); // 273.15 K
var P0 = PhysicalConstants.Generic.StandardAtmosphericPressure<double>(); // 101,325 Pa
// Conversion factors with derived validation
var ftToM = PhysicalConstants.Generic.FeetToMeters<double>(); // 0.3048 m/ft
var sqFtToSqM = PhysicalConstants.Generic.SquareFeetToSquareMeters<double>(); // Derived: ftToM²
// All constants have comprehensive test coverage ensuring derived values match calculationsThe library includes 80+ physics quantities across 8 scientific domains:
// Kinematics and dynamics
var velocity = Velocity<double>.FromMetersPerSecond(15.0);
var acceleration = Acceleration<double>.FromMetersPerSecondSquared(9.8);
var force = Mass<double>.FromKilograms(10.0) * acceleration; // F = ma
// Work and energy
var work = force * Length<double>.FromMeters(5.0); // W = F⋅d
var power = work / Time<double>.FromSeconds(2.0); // P = W/t// Ohm's law relationships
var voltage = Voltage<double>.FromVolts(12.0);
var current = Current<double>.FromAmperes(2.0);
var resistance = voltage / current; // R = V/I
var power = voltage * current; // P = VI// Thermodynamics
var temp = Temperature<double>.FromCelsius(25.0);
var heat = Heat<double>.FromJoules(1000.0);
var capacity = HeatCapacity<double>.FromJoulesPerKelvin(100.0);
var entropy = heat / temp; // S = Q/T// Chemical calculations
var moles = AmountOfSubstance<double>.FromMoles(0.5);
var molarity = moles / Volume<double>.FromLiters(2.0); // M = n/V
var rate = ReactionRate<double>.FromMolarPerSecond(0.01);// Sound and vibration
var frequency = Frequency<double>.FromHertz(440.0); // A4 note
var wavelength = SoundSpeed<double>.Default / frequency; // λ = v/f
var intensity = SoundIntensity<double>.FromWattsPerSquareMeter(1e-6);// Nuclear physics
var activity = RadioactiveActivity<double>.FromBecquerels(1000.0);
var dose = AbsorbedDose<double>.FromGrays(0.001);
var exposure = Exposure<double>.FromCoulombsPerKilogram(1e-6);// Photometry and optics
var flux = LuminousFlux<double>.FromLumens(800.0);
var illuminance = flux / Area<double>.FromSquareMeters(4.0); // E = Φ/A
var luminance = Luminance<double>.FromCandelasPerSquareMeter(100.0);// Fluid mechanics
var viscosity = DynamicViscosity<double>.FromPascalSeconds(0.001);
var flowRate = VolumetricFlowRate<double>.FromCubicMetersPerSecond(0.1);
var reynolds = ReynoldsNumber<double>.Calculate(velocity, Length<double>.FromMeters(0.1), viscosity);The library uses a sophisticated bootstrap architecture to resolve circular dependencies:
// BootstrapUnits class provides initial unit definitions during system initialization
// PhysicalDimensions uses BootstrapUnits to define dimensions without circular dependencies
// Units class replaces bootstrap units with full unit definitions after initialization
// This clean separation enables complex type systems while maintaining performanceAll derived physical constants are validated against their fundamental relationships:
// Example: Area conversions are validated to ensure SquareFeetToSquareMeters = FeetToMeters²
[TestMethod]
public void DerivedConstants_AreaConversions_MatchCalculatedValues()
{
var feetToMeters = PhysicalConstants.Conversion.FeetToMeters;
var calculatedSquareFeet = feetToMeters * feetToMeters;
var storedSquareFeet = PhysicalConstants.Conversion.SquareFeetToSquareMeters;
Assert.AreEqual(calculatedSquareFeet, storedSquareFeet, tolerance);
}
// Comprehensive test coverage ensures physical relationships are mathematically correct// Register factories in your DI container
services.AddTransient<ISemanticStringFactory<EmailAddress>, SemanticStringFactory<EmailAddress>>();
// Use in services
public class UserService
{
private readonly ISemanticStringFactory<EmailAddress> _emailFactory;
public UserService(ISemanticStringFactory<EmailAddress> emailFactory)
{
_emailFactory = emailFactory;
}
public async Task<User> CreateUserAsync(string email)
{
// Factory handles validation and throws meaningful exceptions
var validatedEmail = _emailFactory.Create(email);
return new User(validatedEmail);
}
}Comprehensive documentation is available in the docs/ directory:
Extensive examples are available in docs/examples/:
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
This project is licensed under the MIT License - see the LICENSE.md file for details.
Transform your primitive-obsessed code into a strongly-typed, self-validating domain model with ktsu.Semantics.