Design by Contract library for .NET 8+
Based on Java uContract 2.0.1
A modern Design by Contract (DBC) library for .NET. This is a direct port of the Java uContract 2.0.1 library with .NET-specific improvements.
uContract.NET follows Semantic Versioning; the public API is stable as of 1.0.0. Changes between releases are tracked in CHANGELOG.md. The current version is shown by the NuGet badge above.
- Quick Start
- Features
- API Reference - Quick overview (Complete API Docs β)
- Configuration - Environment variables (Detailed Config β)
- Examples - Minimal example (15+ More Examples β)
- Requirements
- Differences from Java Version
- Documentation
- License
dotnet add package uContractusing uContract;
public class BankAccount
{
private decimal _balance;
private string _owner;
public BankAccount(string owner, decimal initialBalance)
{
// Preconditions: Validate inputs
Contract.RequireNotNull("Owner", owner);
Contract.Require("Initial balance non-negative", () => initialBalance >= 0);
_owner = owner;
_balance = initialBalance;
}
public void Transfer(decimal amount)
{
// Precondition: Valid amount
Contract.Require("Amount positive", () => amount > 0);
// Capture old state for postcondition
var oldBalance = Contract.Old(() => _balance);
// Business logic
_balance -= amount;
// Postcondition: Balance decreased correctly
Contract.Ensure("Balance decreased", () => _balance == oldBalance - amount);
}
private void CheckInvariant()
{
// Invariant: Balance never negative
Contract.Invariant("Balance non-negative", () => _balance >= 0);
}
}- β Preconditions: Validate method inputs and caller state
- β Postconditions: Verify method results and state changes
- β Invariants: Enforce class-level constraints
- β Check Assertions: Runtime checks for general conditions
- β
State Capture:
Old<T>()for comparing before/after states - β
Field Comparison:
EnsureAssignable<T>()with reflection-based validation
- π Zero overhead when disabled: Lazy evaluation via
Func<bool>delegates - βοΈ Runtime configuration: Control contracts via environment variables
- π― DDD-focused: Designed for domain models and aggregate roots
- π¦ Zero external dependencies: Uses only .NET built-in APIs
- π Thread-safe: AsyncLocal support for async/await
- π Strongly typed: Generic constraints ensure type safety
Traditional Design by Contract tools face a sustainability challenge - most extensions are abandoned or dormant because they require custom compilers/preprocessors that must constantly keep up with language evolution.
Root contracting solves this with a simple utility library approach:
- β No custom tooling - Uses standard C# features only (no IL weaving, no code generation)
- β Language-independent design - Same approach can be ported to Java, Go, Python, Rust
- β
Zero dependencies - Only .NET built-in APIs (
System.Text.Json,System.Reflection)
Learn more:
- π Research paper - Formal foundation, experiments, and comparisons with other DbC tools
- π Architecture Decision Records - Design rationale and trade-offs for each decision
- π DDD Integration Guide - How to apply contracts with DDD patterns
π Complete Documentation: API_REFERENCE.md β configuration, exceptions, best practices, and performance
uContract.NET's public API is organized into 4 categories:
| Category | Methods | Purpose |
|---|---|---|
| Preconditions | Require, RequireNotNull, RequireNotEmpty |
Validate inputs at method entry |
| Postconditions | Ensure, EnsureNotNull, EnsureResult, EnsureImmutableCollection, EnsureAssignable |
Verify results and state changes |
| Invariants | Invariant, InvariantNotNull |
Enforce class-level constraints |
| Helpers | Check, Ignore, Old, Imply, IfAndOnlyIf, FollowsFrom, CheckUnsupportedOperation |
Runtime assertions and utilities |
βοΈ Detailed Configuration: USAGE_EXAMPLES.md - Configuration Examples
Control contract evaluation at runtime via environment variables.
| Variable | Values | Default (Debug) | Default (Release) | Purpose |
|---|---|---|---|---|
DBC |
on/off |
on |
off |
Master switch for all contracts |
DBC_PRE |
on/off |
on |
off |
Preconditions only |
DBC_POST |
on/off |
on |
off |
Postconditions only |
DBC_INV |
on/off |
on |
off |
Invariants only |
DBC_CHECK |
on/off |
on |
off |
Check statements only |
DBC_DOC |
on/off |
off |
off |
Diagnostic output |
Windows (PowerShell):
$env:DBC="on"Linux/macOS:
export DBC=on- Debug builds: All contracts enabled by default
- Release builds: All contracts disabled by default
- Parameter validation: Always executed (even when DBC is disabled)
- Zero overhead: Conditions are never evaluated when disabled (lazy evaluation)
π More Examples: USAGE_EXAMPLES.md (15+ real-world scenarios)
- DDD Quick Start: Minimal aggregate root example β USAGE_EXAMPLES.md
- State Capture: Using
Old<T>()for complex objects β USAGE_EXAMPLES.md - Field Validation: Using
EnsureAssignable<T>()β USAGE_EXAMPLES.md - Real-World Scenarios: Banking, e-commerce, inventory systems β USAGE_EXAMPLES.md
- Complete DDD Guide: Value objects, domain services (10+ patterns) β DDD_INTEGRATION_GUIDE.md
- .NET 8.0 or later
- C# 12 (nullable reference types enabled)
- No external dependencies (uses only .NET built-in APIs)
- β
All reference types (
class,record) - β
All value types (
struct,record struct) - β Generic types with appropriate constraints
- β Collections (IEnumerable, List, Array, ImmutableList, etc.)
uContract.NET maintains semantic parity with the Java version while leveraging modern .NET features.
| Feature | Java | C# |
|---|---|---|
| Method naming | require(), old(), imply() |
Require(), Old(), Imply() |
| Lambda syntax | () -> condition |
() => condition |
| Functional types | BooleanSupplier, Supplier<T> |
Func<bool>, Func<T> |
| Null checks | @NonNull annotation |
where T : class constraint |
| Feature | Java | C# |
|---|---|---|
| Parameter validation | β Not validated | β Always validated (even when DBC off) |
| Exception messages | β Unified format | |
| Async support | β ThreadLocal (no async) | β AsyncLocal (async/await safe) |
| Configuration | β Encapsulated ContractConfiguration class | |
| Nullability | β Compiler-enforced nullable reference types | |
Old<T>() overloads |
TypeReference<T> needed for generics) |
β
Single Old<T>(Func<T>) β reified generics make the type hint unnecessary |
| Description auto-capture | β Annotation mandatory on every call | β
Condition-based methods (Require/Ensure/Invariant/Check) have [CallerArgumentExpression] overloads β compiler captures the condition's source text when description is omitted |
Note on description auto-capture: C# 10's
[CallerArgumentExpression]letsContract.Require(() => balance >= 0)work without a manual description β the compiler embeds the condition's source text at compile time, so the exception message shows exactly what failed. Available on the four condition-based methods; value-based null-check methods (RequireNotNull,RequireNotEmpty,EnsureNotNull,InvariantNotNull) do not have CAE overloads due to C# overload-resolution collisions with their existing generic signatures. See ADR-0018 for the full rationale and limitations.
Note on
Old<T>(): Java'sold(Supplier<T>, TypeReference<T>)overload exists to work around type erasure. .NET's reified generics preserve full generic type information at runtime, soJsonSerializer.Deserialize<T>(json)already receives the closed-generic type with no external hint needed. See ADR-0016 for the full rationale.
Renames to be aware of when porting Java code that uses uContract:
| Java (2.0.1) | uContract.NET | Notes |
|---|---|---|
reject() |
Ignore() |
Renamed for semantic clarity |
ClassInvariantViolationException |
InvariantViolationException |
Shorter name; same role in the exception hierarchy |
old(Supplier<T>, TypeReference<T>) |
(removed) | Use Old<T>(Func<T>) β no type hint needed (ADR-0016) |
Exception messages differ. Java throws with the raw description text
("x positive"); uContract.NET prefixes the contract type
("Precondition violated: x positive"). If your tests or log parsers assert
on exception messages, update them accordingly. The unprefixed description
remains available via the exception's Description property.
Environment variables (DBC, DBC_PRE, DBC_POST, DBC_INV, DBC_CHECK) keep
the same names and semantics as the Java version.
Java:
// Java
require("x positive", () -> x > 0);
var oldBalance = old(() -> balance);
ensure("balance decreased", () -> balance < oldBalance);C#:
// C#
Contract.Require("x positive", () => x > 0);
var oldBalance = Contract.Old(() => balance);
Contract.Ensure("balance decreased", () => balance < oldBalance);-
π API_REFERENCE.md - API reference
- Configuration, exception hierarchy, best practices, performance
- Per-method signatures and XML docs available via IDE IntelliSense
-
π USAGE_EXAMPLES.md - Real-world examples
- 15+ practical scenarios (banking, e-commerce, inventory, user management)
- Quick DDD examples with links to complete guide
- Configuration examples and testing patterns
-
ποΈ DDD_INTEGRATION_GUIDE.md - DDD integration guide
- 10+ DDD patterns with best practices
- Specifications, repositories
Contributions are welcome!
Before contributing:
- Read the Architecture Decision Records
MIT License β Copyright (c) 2025-2026 uContract.NET Contributors. See LICENSE for details.
This project is a derivative work of the Java uContract library by Teddy Chen and contributors, which is licensed under the Apache License 2.0. See THIRD-PARTY-NOTICES.txt for the required attribution and license text.
This .NET port is based on Java uContract 2.0.1 (GitLab commit: cb1e03f)
- Repository: Java uContract (GitLab)
- Documentation: Javadoc