Epsitec.Bcx.Core
7.3.3.2636
Prefix Reserved
See the version list below for details.
dotnet add package Epsitec.Bcx.Core --version 7.3.3.2636
NuGet\Install-Package Epsitec.Bcx.Core -Version 7.3.3.2636
<PackageReference Include="Epsitec.Bcx.Core" Version="7.3.3.2636" />
<PackageVersion Include="Epsitec.Bcx.Core" Version="7.3.3.2636" />
<PackageReference Include="Epsitec.Bcx.Core" />
paket add Epsitec.Bcx.Core --version 7.3.3.2636
#r "nuget: Epsitec.Bcx.Core, 7.3.3.2636"
#:package Epsitec.Bcx.Core@7.3.3.2636
#addin nuget:?package=Epsitec.Bcx.Core&version=7.3.3.2636
#tool nuget:?package=Epsitec.Bcx.Core&version=7.3.3.2636
Bcx.Core
Base Class Library Extensions (Core Library)
Overview
Bcx.Core is a comprehensive .NET Standard 2.0 library that provides powerful extensions and utilities for the Base Class Library (BCL). It offers a rich set of functionality including functional programming patterns, reactive extensions, advanced collection types, I/O utilities, console enhancements, and much more.
Copyright ยฉ 2013-2025, EPSITEC SA, CH-1400 Yverdon-les-Bains, Switzerland
Author & Maintainer: Roger VUISTINER
Installation
Install via NuGet Package Manager:
Install-Package Epsitec.Bcx.Core
Or via .NET CLI:
dotnet add package Epsitec.Bcx.Core
Target Framework
- .NET Standard 2.0 - Compatible with .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+, and later versions
Quick Start
using Bcx;
using Bcx.Collections;
using Bcx.Threading;
// Use Option monad for safe null handling
Option<User> user = GetUser(id);
var userName = user.Match(
some: u => u.Name,
none: () => "Unknown"
);
// Repository with automatic reference counting
var repo = new Repository<string, DbConnection>();
using (var conn = repo.GetOrAdd("main", _ => new SqlConnection(connStr)))
{
// Use connection - automatically managed
}
// Enhanced console with colors
using (ConsoleEx.InitializeInvariant())
{
var success = new ColoredText("โ Done", EscapeSequence.Green);
Console.WriteLine(success);
}
// Task with timeout
await LongRunningTask().Timeout(TimeSpan.FromSeconds(30));
// Parse and use ranges
var range = Range.Parse("1.0..2.0");
var inRange = range.Contains("1.5"); // true
Table of Contents
Key Features
๐ฏ Functional Programming
- Function Composition:
After,Thenmethods for composing functions - Currying & Uncurrying: Transform functions between curried and uncurried forms
- Partial Application: Apply arguments incrementally
- Memoization: Cache function results with
MemoizeCache - Option Monad: Robust optional value handling (inspired by language-ext)
// Option monad example
Option<int> GetValue() => Option.Some(42);
Option<int> noValue = Option.None;
๐ฆ Advanced Collections
- Repository<TKey, TValue>: Thread-safe, reference-counted resource management with deterministic disposal
- MultiValueDictionary: Dictionary that maps keys to multiple values
- IndexedHashSet: Hash set with index-based access
- LookupHashSetTable & LookupListTable: Efficient lookup tables
- WeakReferenceDictionary: Dictionary with weak references for memory management
- ReferenceEqualityComparer: Compare objects by reference equality
- AnonymousComparer & AnonymousEqualityComparer: Create comparers from lambda expressions
๐ Range System
Comprehensive range support with a flexible grammar:
Range.Parse("1") // Degenerated: single value
Range.Parse("..3") // Right-bounded: before 3
Range.Parse("1..") // Left-bounded: since 1
Range.Parse("1..3") // Bounded: since 1 but before 3
Range.Parse("") // Infinity: for all
Range.Parse("1..3,6,8..") // Set: multiple ranges
๐งต Threading & Async
- Task Extensions:
Timeout, LINQ-style operators (Select,SelectMany,Where) - ValueTask Extensions: Enhanced ValueTask support
- SessionMutex: Named mutex for cross-process synchronization
- Singleton<T>: Thread-safe singleton pattern implementation
- ReaderWriterLock Extensions: Simplified lock management
await task.Timeout(TimeSpan.FromSeconds(30));
๐ฅ๏ธ Console Enhancement
- VT100 Support: ANSI escape sequence handling
- ColoredText: Rich colored text with automatic escape sequence management
- TextSpan: Advanced text layout with padding, tabs, and width calculations
- Terminal: Cross-platform terminal utilities
- ConsoleEx: Enhanced console initialization with culture and encoding support
using (ConsoleEx.InitializeInvariant())
{
// Console configured with invariant culture and proper encoding
}
๐ I/O Utilities
- PathEx: Extended path manipulation (sanitization, wildcards, URI creation)
- FileEx & DirectoryEx: Enhanced file and directory operations
- ConcurrentFile: Thread-safe file operations
- StreamExtensions: Stream reading and manipulation utilities
- DirectoryFinder: Recursive directory traversal
- PathSpec: Path specification and matching
๐ Security
- FileHash: SHA256 file hashing utilities
- ICertificateProvider: Certificate management interface
- ISecure: Security contract interface
- SignatureException & EncryptionException: Security-related exceptions
๐ Type Conversion
- TypeConverter<T1, T2>: Bidirectional type conversion facade
- EnumConverter: Convert between enums by name, map, or cast
- IEnumConverter: Extensible enum conversion interface
TypeConverters.Register<Celsius, Fahrenheit>(
c => new Fahrenheit(c.Value * 9/5 + 32),
f => new Celsius((f.Value - 32) * 5/9)
);
var fahrenheit = TypeConverter<Celsius, Fahrenheit>.Convert(celsius);
๐ LINQ Extensions
- Traverse: Non-recursive graph traversal
- Chunk: Split sequences into chunks
- Throw/Trace: Observable and enumerable debugging extensions
๐ String & Text
- StringExtensions:
HasValue,HasContent,SplitPreservingDelimiters, and more - StringBuilder Extensions: Enhanced string building
- SynonymsTable: Manage text synonyms and aliases
- StringWriterWithEncoding: String writer with custom encoding
๐ข Math & Numerics
- MathEx: Extended math operations including double-precision utilities
- DecimalExtensions: Decimal number operations
๐ค๏ธ Error Handling
- Bumpy<T>: Result type for railway-oriented programming that encapsulates either a successful value or an exception, providing type-safe error handling without throwing exceptions immediately
โฑ๏ธ Time Management
- ITimeService: Abstraction for time operations (testable time)
- DualTime: Work with multiple time representations
- TimeSpan Extensions: Enhanced timespan operations
๐จ Enums
- EnumEx:
GetValues<T>,GetFlags<T>,GetDescriptionfrom attributes - IEnumConverter: Convert between enum types
๐งฉ System Extensions
- DateTimeExtensions & DateTimeOffsetExtensions: Date/time manipulation
- DisposableExtensions: Reference counting for IDisposable (
AddRef) - ExceptionExtensions: Enhanced exception handling
- ObjectExtensions: Common object utilities
- TypeExtensions: Type introspection and manipulation
- UriExtensions: URI utilities
- VersionExtensions: Version comparison and parsing
๐ Command Line
- CommandLineSplitter: Parse command-line arguments
๐ฏ Filters
- Predicate & PredicateFilter: Functional filtering
- RegexFilter: Regular expression-based filtering
๐งฐ Diagnostics
- TraceMT: Multi-threaded tracing
- ObservableExtensions: Throw and trace for observables
- LinqExtensions: Throw and trace for LINQ queries
๐ฆ Resources
- ResourceLoader: Load embedded resources
Additional Features
๐ฏ System Extensions
DisposableExtensions - Reference counting for IDisposable:
var resource = new MyDisposable();
resource.AddRef(); // Increment reference count
// ... use resource ...
resource.Release(); // Decrement and dispose when zero
TypeExtensions - Type introspection:
var isNullable = typeof(int?).IsNullable();
var underlyingType = typeof(int?).GetNullableUnderlyingType();
bool implements = typeof(MyClass).Implements<IDisposable>();
ExceptionExtensions - Enhanced exception handling:
try
{
RiskyOperation();
}
catch (Exception ex)
{
var innerMost = ex.GetInnermostException();
var allMessages = ex.GetAllMessages();
Log(allMessages);
}
๐ Console Tables
Batch Mode - Build complete tables:
var table = new TableBuilder()
.SetWidth(80)
.UsePreset(() => TableStyle.Presets.Modern)
.AddColumn("Name", 20)
.AddColumn("Status", 15)
.AddRow("Task 1", "Complete")
.AddRow("Task 2", "Pending")
.Build();
Console.WriteLine(table.Render());
Streaming Mode - Dynamic row rendering:
var renderer = new TableRenderer()
.SetWidth(100)
.UsePreset(() => TableStyle.Presets.Box)
.AddColumn("Time", 12)
.AddColumn("Event", 60);
renderer.RenderHeader(Console.Out);
foreach (var evt in GetEvents())
{
renderer.RenderRow(Console.Out, evt.Time, evt.Description);
}
renderer.RenderFooter(Console.Out);
Table Style Presets:
TableStyle.Presets.Default- Simple ASCII bordersTableStyle.Presets.Modern- Clean Unicode bordersTableStyle.Presets.Box- Box drawing charactersTableStyle.Presets.Minimal- Minimal bordersTableStyle.Presets.None- No borders
๐ Filters
PredicateFilter - Combine multiple predicates:
var filter = new PredicateFilter<int>()
.Add(x => x > 0)
.Add(x => x % 2 == 0);
var filtered = numbers.Where(filter.Matches);
RegexFilter - Regular expression filtering:
var filter = new RegexFilter(@"^\d{3}-\d{4}$");
var phoneNumbers = items.Where(filter.IsMatch);
๐ฆ Resources
ResourceLoader - Load embedded resources:
// Load string resource
var json = ResourceLoader.LoadString(
assembly,
"MyApp.Resources.config.json"
);
// Load binary resource
var bytes = ResourceLoader.LoadBytes(
assembly,
"MyApp.Resources.icon.png"
);
๐ข Random Extensions
var random = new Random();
// Generate random boolean
bool coinFlip = random.NextBool();
// Random element from collection
var item = collection.RandomElement(random);
// Shuffle collection
var shuffled = items.Shuffle(random);
๐งฉ Entity and Identity
// Entity with identity
public class User : Entity<int>
{
public User(int id) : base(id) { }
public string Name { get; set; }
}
// Entities with same ID are equal
var user1 = new User(1) { Name = "Alice" };
var user2 = new User(1) { Name = "Bob" };
bool areEqual = user1.Equals(user2); // true
Dependencies
This library leverages high-quality NuGet packages:
- Microsoft.CSharp (Latest) - Dynamic language runtime support and dynamic type features
- System.Collections.Immutable (Latest) - Immutable collection types for thread-safe operations
- System.Interactive (Latest) - Interactive LINQ extensions (Ix)
- System.IO.Pipelines (Latest) - High-performance I/O primitives for efficient data streaming
- System.Reactive (Latest) - Reactive Extensions (Rx) for composing asynchronous and event-based programs
- System.Text.Json (Latest) - High-performance JSON serialization and deserialization
All dependencies target .NET Standard 2.0 for maximum compatibility across .NET implementations.
Comprehensive Code Examples
๐ฏ Option Monad - Complete Examples
using Bcx;
// Creating optional values
Option<int> some = Option.Some(42);
Option<int> none = Option.None<int>();
Option<string> empty = Option.none; // Using the static instance
// Implicit conversions
Option<string> fromValue = "Hello";
Option<int> fromNone = Option.none;
// Safe navigation with Map
Option<string> userName = GetUser(userId)
.Map(user => user.Name)
.Map(name => name.ToUpper());
// Pattern matching
var message = FindUser(42).Match(
some: user => $"Found: {user.Name}",
none: () => "User not found"
);
// LINQ query syntax with SelectMany
var result = from user in GetUser(userId)
from address in GetAddress(user.Id)
select $"{user.Name} lives at {address.Street}";
// Combining multiple options
var combined = Option.Some(10)
.Bind(x => Option.Some(x * 2))
.Bind(x => x > 15 ? Option.Some(x) : Option.None<int>());
// Extract value safely
if (option.IsSome)
{
var value = option.GetValue();
Console.WriteLine(value);
}
// Convert to nullable
int? nullable = option.ToNullable();
// Execute action only if some
option.IfSome(value => Console.WriteLine($"Value: {value}"));
public Option<User> GetUser(int id)
{
var user = database.Users.FirstOrDefault(u => u.Id == id);
return Option.Optional(user);
}
๐ค๏ธ Bumpy<T> - Railway-Oriented Programming
using Bcx;
// Create results
var success = new Bumpy<int>(42);
var failure = new Bumpy<int>(new InvalidOperationException("Failed"));
// Using factory method (type inferred)
var result = Bumpy.Create("Hello");
// Real-world example: safe division
public Bumpy<decimal> CalculateRatio(decimal numerator, decimal denominator)
{
try
{
if (denominator == 0)
return new Bumpy<decimal>(new DivideByZeroException("Cannot divide by zero"));
return new Bumpy<decimal>(numerator / denominator);
}
catch (Exception ex)
{
return new Bumpy<decimal>(ex);
}
}
// Using Bumpy results
var ratio = CalculateRatio(100, 5);
if (ratio.Succeeded)
{
Console.WriteLine($"Ratio: {ratio.Value:F2}");
}
else
{
Console.WriteLine($"Error: {ratio.Exception.Message}");
LogError(ratio.Exception);
}
// Chaining operations
public Bumpy<Order> ProcessOrder(OrderRequest request)
{
var validation = ValidateOrder(request);
if (validation.Failed) return new Bumpy<Order>(validation.Exception);
var order = CreateOrder(validation.Value);
if (order.Failed) return order;
var saved = SaveOrder(order.Value);
return saved;
}
๐ฆ Repository Pattern with Reference Counting
using Bcx.Collections;
// Create repository for managing database connections
var repository = new Repository<string, DatabaseConnection>();
// Get or add connection (ref count incremented automatically)
var conn1 = repository.GetOrAdd("main", key => new DatabaseConnection($"Server={key}"));
var conn2 = repository.GetOrAdd("main", key => new DatabaseConnection($"Server={key}")); // Returns same instance
// Multiple references to same connection
Console.WriteLine($"RefCount: {repository.Count}"); // Still 1 unique connection
// Release when done (ref count decremented)
repository.Release("main"); // RefCount now 1
repository.Release("main"); // RefCount now 0, connection disposed
// Working with multiple resources
var dbRepo = new Repository<string, DatabaseConnection>();
using (var conn = dbRepo.GetOrAdd("main", _ => new DatabaseConnection("main")))
{
// Use connection
var data = conn.Query("SELECT * FROM Users");
// Automatically released when exiting using block
}
// Subscribe to repository changes
repository.Subscribe(kv => Console.WriteLine($"Changed: {kv.Key}"));
// Check if resource exists
if (repository.Contains("main"))
{
Console.WriteLine("Main connection exists");
}
๐ Ranges - Comprehensive Range System
using Bcx;
// Parse different range formats
var single = Range.Parse("5"); // Single value: 5
var leftBound = Range.Parse("10.."); // From 10 onwards
var rightBound = Range.Parse("..20"); // Up to (but not including) 20
var bounded = Range.Parse("10..20"); // From 10 to 20 (exclusive end)
var infinity = Range.Parse(""); // All values
var set = Range.Parse("1..5,10,15..20"); // Multiple ranges
// Using ranges for filtering
var versions = new[] { "1.0", "2.0", "2.5", "3.0", "4.0" };
var versionRange = Range.Parse("2.0..4.0");
// Check if value is in range
bool isInRange = versionRange.Contains("2.5");
// Filter collections
var filteredVersions = versions.Where(v => versionRange.Contains(v));
// Version filtering example
public bool IsCompatibleVersion(string version, string rangeSpec)
{
var range = Range.Parse(rangeSpec);
return range.Contains(version);
}
// Usage
var isCompatible = IsCompatibleVersion("2.5.1", "2.0..3.0"); // true
๐ฅ๏ธ Console Enhancement with Tables and Colors
using Bcx;
// Initialize console with proper encoding and VT100 support
using (ConsoleEx.InitializeInvariant())
{
// Colored text
var success = new ColoredText("โ Success", EscapeSequence.Green);
var error = new ColoredText("โ Error", EscapeSequence.Red);
var warning = new ColoredText("โ Warning", EscapeSequence.Yellow);
Console.WriteLine(success.PadRight(50));
Console.WriteLine(error.PadRight(50));
Console.WriteLine(warning.PadRight(50));
// Shrink long text with ellipsis
var longText = new ColoredText("This is a very long text", EscapeSequence.Cyan);
Console.WriteLine(longText.Shrink(10)); // "This is aโฆ"
// Create beautiful console tables
var table = new TableBuilder()
.SetWidth(80)
.UsePreset(() => TableStyle.Presets.Modern)
.AddColumn("Name", 20)
.AddColumn("Age", 10)
.AddColumn("Email", 30)
.AddRow("Alice", "30", "alice@example.com")
.AddRow("Bob", "25", "bob@example.com")
.AddRow("Charlie", "35", "charlie@example.com")
.Build();
Console.WriteLine(table.Render());
// Dynamic table building with elastic columns
var dynamicTable = new TableBuilder()
.SetWidth(100)
.SetBorderStyle(BorderStyle.Double)
.AddColumn("ID", 5, fixed: true)
.AddColumn("Description", 0, fixed: false) // Elastic column
.AddColumn("Status", 15, fixed: true)
.AddRow("1", "Process data from source", "โ Complete")
.AddRow("2", "Validate and transform records", "โณ Running")
.AddRow("3", "Export results to destination", "โธ Pending")
.Build();
Console.WriteLine(dynamicTable.Render());
// Stream rows dynamically
var renderer = new TableRenderer()
.SetWidth(80)
.UsePreset(() => TableStyle.Presets.Minimal)
.AddColumn("Time", 10)
.AddColumn("Message", 60);
renderer.RenderHeader(Console.Out);
foreach (var logEntry in GetLogEntries())
{
renderer.RenderRow(Console.Out, logEntry.Time, logEntry.Message);
}
renderer.RenderFooter(Console.Out);
}
// Advanced VT100 usage
VT100.Enable();
Console.WriteLine($"{EscapeSequence.Bold}{EscapeSequence.Green}Bold Green Text{EscapeSequence.Reset}");
Console.WriteLine($"{EscapeSequence.Underline}Underlined{EscapeSequence.Reset}");
๐งต Threading - Task Extensions and LINQ
using Bcx.Threading;
using System.Threading.Tasks;
// Task timeout
try
{
await LongRunningOperation().Timeout(TimeSpan.FromSeconds(30));
}
catch (TimeoutException)
{
Console.WriteLine("Operation timed out");
}
// LINQ-style task composition
var result = await GetUserId()
.Select(id => GetUser(id))
.Select(user => user.Name);
// Async LINQ query syntax
var userName = await (
from id in Task.FromResult(42)
from user in GetUserAsync(id)
from dept in GetDepartmentAsync(user.DepartmentId)
select $"{user.Name} ({dept.Name})"
);
// Where clause with tasks
var validUsers = await GetUserAsync(userId)
.Where(user => user.Age >= 18);
// Singleton pattern (thread-safe)
var config = Singleton<Configuration>.Instance;
// Cross-process synchronization with SessionMutex
using (var mutex = new SessionMutex("MyApp_SingleInstance"))
{
if (mutex.WaitOne(TimeSpan.FromSeconds(5)))
{
try
{
// Only one instance can enter this block
RunApplication();
}
finally
{
mutex.ReleaseMutex();
}
}
else
{
Console.WriteLine("Another instance is already running");
}
}
// ReaderWriterLock extensions
using (readerWriterLock.ReadLock())
{
// Multiple readers allowed
var data = ReadData();
}
using (readerWriterLock.WriteLock())
{
// Exclusive write access
WriteData(newData);
}
๐ LINQ Extensions - Traverse and More
using Bcx.Linq;
// Traverse hierarchies without recursion
public class TreeNode
{
public string Name { get; set; }
public List<TreeNode> Children { get; set; } = new();
}
var root = BuildTree();
// Get all nodes in the tree
var allNodes = root.Traverse(node => node.Children);
// Find all files in directory tree
var rootDir = new DirectoryInfo(@"C:\Projects");
var allFiles = rootDir.Traverse(
dir => dir.GetDirectories(),
traversalPredicate: dir => !dir.Name.StartsWith(".")
).SelectMany(dir => dir.GetFiles());
// Traverse with conditions
var publicNodes = root.Traverse(
node => node.Children,
traversalPredicate: node => node.IsPublic,
ignorePredicate: node => node.IsHidden
);
// Chunk sequences for batch processing
var items = Enumerable.Range(1, 100);
var batches = items.Chunk(10);
foreach (var batch in batches)
{
await ProcessBatchAsync(batch);
}
// Trace LINQ queries for debugging
var result = items
.Where(x => x > 10)
.Trace("After filter")
.Select(x => x * 2)
.Trace("After select")
.ToList();
๐ Type Conversion System
using Bcx;
using Bcx.Converters;
// Register bidirectional converter
TypeConverters.Register<Celsius, Fahrenheit>(
c => new Fahrenheit(c.Value * 9 / 5 + 32),
f => new Celsius((f.Value - 32) * 5 / 9)
);
// Use converter
var celsius = new Celsius(100);
var fahrenheit = TypeConverter<Celsius, Fahrenheit>.Convert(celsius);
var backToCelsius = TypeConverter<Fahrenheit, Celsius>.Convert(fahrenheit);
// Enum conversion by name
public enum SourceStatus { Active, Inactive, Pending }
public enum TargetStatus { Active, Inactive, Pending, Archived }
var source = SourceStatus.Active;
var target = EnumConverter.ConvertByName<SourceStatus, TargetStatus>(source);
// Enum conversion with mapping
var mapping = new Dictionary<SourceStatus, TargetStatus>
{
[SourceStatus.Active] = TargetStatus.Active,
[SourceStatus.Inactive] = TargetStatus.Archived,
[SourceStatus.Pending] = TargetStatus.Pending
};
var converted = EnumConverter.ConvertByMap(source, mapping);
// Get all enum values
var allStatuses = EnumEx.GetValues<SourceStatus>();
// Get enum flags
var flags = EnumEx.GetFlags<FileAttributes>();
๐ IO Utilities - Path and File Operations
using Bcx.IO;
// Sanitize file names
var fileName = "My<File>Name?.txt";
var safe = PathEx.GetSanitizedFileName(fileName); // "My_File_Name_.txt"
// Safe file name extraction
var fullPath = @"C:\Projects\MyApp\file.txt";
var name = PathEx.GetFileNameSafe(fullPath); // "file.txt"
// Create URI from path
var uri = PathEx.CreateUriFromPath(@"C:\Projects\MyApp");
// Get full path with custom base directory
var relativePath = "../data/config.json";
var baseDir = @"C:\Projects\MyApp\bin";
var fullPath2 = PathEx.GetFullPath(relativePath, baseDir);
// Concurrent file operations (thread-safe)
await ConcurrentFile.WriteAllTextAsync("log.txt", "Log entry\n");
var content = await ConcurrentFile.ReadAllTextAsync("log.txt");
// Directory traversal
var finder = new DirectoryFinder(@"C:\Projects");
var allDirs = finder.GetDirectories("*", recursive: true);
// File hash computation
var hash = FileHash.ComputeSha256(@"C:\path\to\file.exe");
var isValid = FileHash.VerifySha256(filePath, expectedHash);
// Stream extensions
using var stream = File.OpenRead("data.bin");
var bytes = stream.ReadAllBytes();
var text = stream.ReadAllText(Encoding.UTF8);
๐จ Functional Programming Patterns
using Bcx.Functional;
// Function composition
Func<int, int> addTwo = x => x + 2;
Func<int, int> multiplyThree = x => x * 3;
Func<int, int> square = x => x * x;
// Chain functions: (5 + 2) * 3 = 21
var composed = addTwo.Then(multiplyThree);
var result1 = composed(5);
// Or use After: multiplyThree after addTwo
var composed2 = multiplyThree.After(addTwo);
var result2 = composed2(5);
// Complex composition
var pipeline = addTwo
.Then(multiplyThree)
.Then(square);
var result3 = pipeline(5); // ((5 + 2) * 3)ยฒ = 441
// Currying - transform multi-parameter function to chain of single-parameter functions
Func<int, int, int> add = (x, y) => x + y;
var curriedAdd = add.Curry();
var add5 = curriedAdd(5);
var result4 = add5(10); // 15
// Partial application
Func<int, int, int, int> sum3 = (a, b, c) => a + b + c;
var sum10AndPartial = sum3.PartialApply(10); // Fix first parameter
var result5 = sum10AndPartial(20, 30); // 10 + 20 + 30 = 60
// Memoization (cache function results)
Func<int, int> expensiveCalculation = x =>
{
Thread.Sleep(1000); // Simulate expensive operation
return x * x;
};
// Results are cached
var memoized = expensiveCalculation.Memoize();
var r1 = memoized(5); // Takes 1 second
var r2 = memoized(5); // Instant (cached)
var r3 = memoized(10); // Takes 1 second
๐ String and Text Processing
using Bcx;
using Bcx.Text;
// String extensions
string text = " Hello ";
bool hasValue = text.HasValue(); // true (not null or empty)
bool hasContent = text.HasContent(); // true (not null, empty, or whitespace)
string? nullText = null;
bool hasValue2 = nullText.HasValue(); // false
// Split preserving delimiters
var input = "a,b;c,d";
var parts = input.SplitPreservingDelimiters(new[] { ',', ';' });
// Result: ["a", ",", "b", ";", "c", ",", "d"]
// Synonyms table for text normalization
var synonyms = new SynonymsTable();
synonyms.Add("color", "colour", "Color", "Colour");
synonyms.Add("center", "centre", "Center", "Centre");
var normalized1 = synonyms.Normalize("colour"); // "color"
var normalized2 = synonyms.Normalize("centre"); // "center"
// Check if text is synonym
bool isSynonym = synonyms.IsSynonym("colour", "color"); // true
// Global synonyms tables
SynonymsTables.Default.Add("gray", "grey");
// StringBuilder extensions
var sb = new StringBuilder();
sb.AppendLineInvariant($"Value: {42}");
sb.AppendFormatInvariant("Total: {0:F2}", 123.45);
// String writer with custom encoding
using var writer = new StringWriterWithEncoding(Encoding.UTF8);
writer.WriteLine("UTF-8 content");
var content = writer.ToString();
๐ Security - File Hashing and Certificates
using Bcx.Security;
// Compute SHA256 hash of file
var filePath = @"C:\Downloads\installer.exe";
var hash = FileHash.ComputeSha256(filePath);
Console.WriteLine($"SHA256: {hash}");
// Verify file integrity
var expectedHash = "ABC123...";
bool isValid = FileHash.VerifySha256(filePath, expectedHash);
if (isValid)
{
Console.WriteLine("File integrity verified");
}
else
{
throw new SignatureException("File has been tampered with");
}
// Certificate provider interface
public class MyCertificateProvider : ICertificateProvider
{
public X509Certificate2 GetCertificate(string name)
{
// Load certificate from store or file
return LoadCertificate(name);
}
}
// Use in secure operations
public class SecureService : ISecure
{
public bool IsSecure => true;
public void SecureOperation()
{
// Implement secure logic
}
}
๐ Command Line Parsing
using Bcx.CommandLine;
// Parse command line with quoted arguments
var commandLine = "--config \"C:\\My Documents\\app.config\" --verbose --port 8080";
var args = CommandLineSplitter.Split(commandLine);
// Result: ["--config", "C:\\My Documents\\app.config", "--verbose", "--port", "8080"]
// Handle complex escaping
var complex = "app.exe \"arg with spaces\" 'single quotes' --flag=value";
var parsed = CommandLineSplitter.Split(complex);
// Build command lines
var builder = new StringBuilder();
builder.Append("dotnet");
builder.Append(" run");
builder.Append(" --project \"My Project.csproj\"");
var command = builder.ToString();
๐ข Math Extensions
using Bcx;
using Bcx.System;
// Double precision utilities
double value = 1.23456789;
var rounded = MathEx.Round(value, 2); // 1.23
// Check if doubles are approximately equal
bool areEqual = MathEx.AreEqual(1.0000001, 1.0000002, tolerance: 0.001);
// Decimal extensions
decimal amount = 123.456m;
var rounded2 = amount.Round(2); // 123.46
var truncated = amount.Truncate(2); // 123.45
// Clamp values
int clamped = 150.Clamp(0, 100); // 100
// Integer extensions
int number = 42;
bool isEven = number.IsEven();
bool isOdd = number.IsOdd();
โฑ๏ธ Time Management
using Bcx.Time;
// Testable time service
public class MyService
{
private readonly ITimeService timeService;
public MyService(ITimeService timeService)
{
this.timeService = timeService;
}
public void LogAction()
{
var now = timeService.Now;
Console.WriteLine($"Action at: {now}");
}
}
// In production
var service = new MyService(new SystemTimeService());
// In tests
var mockTime = new MockTimeService(new DateTime(2025, 1, 1));
var testService = new MyService(mockTime);
// DualTime for multiple time representations
var dualTime = new DualTime(DateTime.UtcNow, DateTimeOffset.Now);
// TimeSpan extensions
var duration = TimeSpan.FromMinutes(90);
var formatted = duration.ToHumanReadable(); // "1 hour 30 minutes"
๐ Diagnostics and Debugging
using Bcx.Diagnostics;
// Multi-threaded tracing
TraceMT.WriteLine("Application started");
TraceMT.WriteLineIf(debugMode, "Debug info");
// Trace LINQ operations
var items = Enumerable.Range(1, 100)
.Where(x => x % 2 == 0)
.Trace("After filter")
.Select(x => x * x)
.Trace("After select")
.ToList();
// Trace observables
var observable = Observable.Range(1, 10)
.Where(x => x > 5)
.Trace("Filtered values")
.Select(x => x * 2);
// Throw exceptions in pipelines
var result = items
.Where(x => x > 0)
.Throw<InvalidOperationException>(x => x < 0, "Negative value found")
.ToList();
Building and Packaging
Creating a NuGet Package
To create a NuGet package, use the provided batch script:
zou-nuget.bat
This script automates the complete packaging workflow:
- Build the project with
Pack=trueMSBuild property - Create the NuGet package (
.nupkg) in..\.nupkgdirectory - Create the symbol package (
.snupkg) with embedded sources - Sign the package (if signing is configured)
- Push to nuget.org (if credentials are configured)
Manual Build
Alternatively, build manually with MSBuild:
dotnet build -c Release /p:Pack=true
Or using MSBuild directly:
msbuild Bcx.Core.csproj /p:Configuration=Release /p:Pack=true
Package Configuration
When building with Pack=true, the following features are enabled:
Package Metadata:
- PackageId:
Epsitec.Bcx.Core - Title: Base Class Library Extensions (Core Library)
- License: MIT License Expression
- Icon: Bcx.Icon.png (embedded)
- README: README.md (this file, embedded)
Debug Experience:
- Embedded sources - All source files embedded in
.snupkgfor step-through debugging - Portable PDB - Debug symbols in portable format for cross-platform debugging
- Source Link - Links to source repository for online source viewing
Build Quality:
- Deterministic builds - Reproducible builds with same input = same output (CI/CD friendly)
- Compiler-generated files - Output to
obj\Generatedfor inspection - Continuous Integration - Optimized for automated build environments
Package Contents
The NuGet package includes:
- โ
Compiled library (
Bcx.Core.dll) - โ
XML documentation (
Bcx.Core.xml) - โ Package icon
- โ README file
- โ
Symbol package (
.snupkg) with embedded sources
Project Structure
Bcx.Core/
โโโ Adornment/ # Text coloring and formatting
โ โโโ ColoredText.cs # Rich colored text with ANSI escape sequences
โ โโโ ShrinkHelpers.cs # Text shrinking utilities
โ
โโโ Collections/ # Advanced collection types
โ โโโ Repository.cs # Thread-safe reference-counted resource management
โ โโโ MultiValueDictionary.cs # Dictionary mapping keys to multiple values
โ โโโ IndexedHashSet.cs # Hash set with index-based access
โ โโโ LookupHashSetTable.cs # Efficient hash set lookup table
โ โโโ LookupListTable.cs # Efficient list lookup table
โ โโโ WeakReferenceDictionary.cs # Dictionary with weak references
โ โโโ AnonymousComparer.cs # Create comparers from lambdas
โ โโโ AnonymousEqualityComparer.cs # Create equality comparers from lambdas
โ
โโโ CommandLine/ # Command-line parsing
โ โโโ CommandLineSplitter.cs # Parse command-line arguments
โ
โโโ Console/ # Console enhancements and VT100
โ โโโ ConsoleEx.cs # Enhanced console initialization
โ โโโ VT100.cs # ANSI escape sequence support
โ โโโ EscapeSequence.cs # Predefined color/formatting codes
โ โโโ Terminal.cs # Cross-platform terminal utilities
โ โโโ Tables/ # Console table rendering
โ โ โโโ TableBuilder.cs # Fluent API for building tables
โ โ โโโ TableRenderer.cs # Dynamic row streaming
โ โ โโโ TableStyle.cs # Table styling and presets
โ โ โโโ Table.cs # Complete rendered table
โ โโโ Formatting/ # Text layout and formatting
โ โโโ TextSpan.cs # Advanced text layout with width calculations
โ โโโ TextSegment.cs # Text segment representation
โ โโโ TextAlignment.cs # Text alignment options
โ
โโโ Converters/ # Type conversion utilities
โ โโโ TypeConverter.cs # Bidirectional type conversion
โ โโโ TypeConverters.cs # Global converter registry
โ โโโ EnumConverter.*.cs # Enum conversion (ByName, ByMap, Cast)
โ โโโ ITypeConverter.cs # Converter interface
โ
โโโ Diagnostics/ # Tracing and debugging
โ โโโ TraceMT.cs # Multi-threaded tracing
โ โโโ LinqExtensions.Trace.cs # Trace LINQ queries
โ โโโ LinqExtensions.Throw.cs # Throw in LINQ pipelines
โ โโโ ObservableExtensions.*.cs # Observable tracing/throwing
โ
โโโ Enums/ # Enum utilities
โ โโโ EnumEx.cs # GetValues, GetFlags, GetDescription
โ โโโ IEnumConverter.cs # Enum conversion interface
โ
โโโ Exceptions/ # Custom exceptions
โ โโโ ValueIsNoneException.cs
โ โโโ ValueIsNullException.cs
โ โโโ ResultIsNullException.cs
โ โโโ SomeNotInitalizedException.cs
โ
โโโ Filters/ # Filtering utilities
โ โโโ Predicate.cs # Functional predicates
โ โโโ PredicateFilter.cs # Predicate-based filtering
โ โโโ RegexFilter.cs # Regex-based filtering
โ
โโโ Functional/ # Functional programming patterns
โ โโโ FunctionExtensions.cs # Composition, currying, partial application
โ โโโ MemoizeCache.cs # Function result memoization
โ
โโโ IO/ # File and directory utilities
โ โโโ PathEx.cs # Extended path manipulation
โ โโโ FileEx.cs # Enhanced file operations
โ โโโ DirectoryEx.cs # Enhanced directory operations
โ โโโ ConcurrentFile.cs # Thread-safe file operations
โ โโโ StreamExtensions.cs # Stream utilities
โ โโโ DirectoryFinder.cs # Recursive directory traversal
โ โโโ PathSpec.cs # Path specification and matching
โ
โโโ Json/ # JSON serialization
โ โโโ SerializerOptions.cs # JSON serializer configuration
โ
โโโ Linq/ # LINQ extensions
โ โโโ LinqExtensions.cs # Traverse, Chunk, Closure
โ
โโโ Optional/ # Option monad
โ โโโ Option.cs # Option<T>, Some<T>, None<T>, IOptional
โ
โโโ Ranges/ # Range system
โ โโโ Range.cs # Range parsing and factory methods
โ โโโ IRange.cs # Range interface
โ โโโ Range.Bounded.cs # Bounded range implementation
โ โโโ Range.LeftBounded.cs # Left-bounded range
โ โโโ Range.RightBounded.cs # Right-bounded range
โ โโโ Range.Infinity.cs # Infinite range
โ โโโ Range.Degenerated.cs # Single-value range
โ โโโ Range.Set.cs # Multiple ranges
โ
โโโ Resources/ # Resource loading
โ โโโ ResourceLoader.cs # Load embedded resources
โ
โโโ Security/ # Security and cryptography
โ โโโ FileHash.cs # SHA256 file hashing
โ โโโ ICertificateProvider.cs # Certificate management interface
โ โโโ ISecure.cs # Security contract
โ โโโ SignatureException.cs # Signature-related exceptions
โ โโโ EncryptionException.cs # Encryption-related exceptions
โ
โโโ System/ # Core BCL extensions
โ โโโ Bumpy.cs # Result type for railway-oriented programming
โ โโโ DisposableExtensions.cs # Reference counting (AddRef, Release)
โ โโโ StringExtensions.cs # HasValue, HasContent, etc.
โ โโโ DateTimeExtensions.cs # Date/time manipulation
โ โโโ TypeExtensions.cs # Type introspection
โ โโโ ExceptionExtensions.cs # Exception handling utilities
โ โโโ ObjectExtensions.cs # Common object utilities
โ โโโ MathEx.cs # Extended math operations
โ โโโ DecimalExtensions.cs # Decimal operations
โ โโโ IntExtensions.cs # Integer utilities
โ
โโโ Text/ # Text processing
โ โโโ StringExtensions.cs # String manipulation
โ โโโ StringBuilder Extensions.cs # StringBuilder enhancements
โ โโโ SynonymsTable.cs # Text synonym management
โ โโโ SynonymsTables.cs # Global synonyms registry
โ โโโ StringWriterWithEncoding.cs # Custom encoding writer
โ
โโโ Threading/ # Threading and async utilities
โ โโโ TaskExtensions.cs # Task timeout, LINQ operators
โ โโโ ValueTaskExtensions.cs # ValueTask extensions
โ โโโ SessionMutex.cs # Cross-process mutex
โ โโโ Singleton.cs # Thread-safe singleton pattern
โ โโโ ReaderWriterLockExtensions.cs # Lock management
โ โโโ FunctionExtensions.cs # Async function utilities
โ
โโโ Time/ # Time management
โโโ ITimeService.cs # Testable time abstraction
โโโ DualTime.cs # Multiple time representations
โโโ TimeMismatchException.cs # Time-related exceptions
Real-World Usage Scenarios
Scenario 1: Building a CLI Application
using Bcx;
using System;
class Program
{
static async Task Main(string[] args)
{
// Initialize console with proper encoding and colors
using (ConsoleEx.InitializeInvariant())
{
// Parse command line
var cmdArgs = CommandLineSplitter.Split(string.Join(" ", args));
// Create beautiful output table
var table = new TableBuilder()
.SetWidth(ConsoleEx.Width)
.UsePreset(() => TableStyle.Presets.Modern)
.AddColumn("Task", 30)
.AddColumn("Status", 15)
.AddColumn("Time", 15);
foreach (var task in GetTasks())
{
var status = task.IsComplete
? new ColoredText("โ Complete", EscapeSequence.Green)
: new ColoredText("โณ Running", EscapeSequence.Yellow);
table.AddRow(task.Name, status.ToString(), task.Duration);
}
Console.WriteLine(table.Build().Render());
}
}
}
Scenario 2: Safe Resource Management
using Bcx.Collections;
public class DatabaseManager
{
private readonly Repository<string, DatabaseConnection> connections;
public DatabaseManager()
{
connections = new Repository<string, DatabaseConnection>();
}
public async Task<TResult> ExecuteAsync<TResult>(
string connectionName,
Func<DatabaseConnection, Task<TResult>> operation)
{
// Automatically manages reference counting
var connection = connections.GetOrAdd(
connectionName,
key => new DatabaseConnection(GetConnectionString(key))
);
try
{
return await operation(connection);
}
finally
{
connections.Release(connectionName);
}
}
}
Scenario 3: Error Handling with Railway-Oriented Programming
using Bcx;
public class OrderProcessor
{
public Bumpy<Order> ProcessOrder(OrderRequest request)
{
// Validate
var validation = ValidateRequest(request);
if (validation.Failed)
return new Bumpy<Order>(validation.Exception);
// Calculate
var calculation = CalculatePricing(validation.Value);
if (calculation.Failed)
return new Bumpy<Order>(calculation.Exception);
// Save
var order = SaveOrder(calculation.Value);
return order;
}
private Bumpy<ValidatedRequest> ValidateRequest(OrderRequest request)
{
try
{
if (string.IsNullOrEmpty(request.CustomerId))
throw new ValidationException("Customer ID required");
return new Bumpy<ValidatedRequest>(new ValidatedRequest(request));
}
catch (Exception ex)
{
return new Bumpy<ValidatedRequest>(ex);
}
}
}
Scenario 4: Hierarchical Data Processing
using Bcx.Linq;
public class FileSystemAnalyzer
{
public IEnumerable<FileInfo> FindAllFiles(string rootPath, string pattern)
{
var rootDir = new DirectoryInfo(rootPath);
// Traverse directory tree without recursion
return rootDir
.Traverse(
dir => dir.GetDirectories(),
traversalPredicate: dir => !dir.Name.StartsWith("."),
ignorePredicate: dir => dir.Name == "node_modules"
)
.SelectMany(dir => dir.GetFiles(pattern));
}
public long CalculateTotalSize(string rootPath)
{
return FindAllFiles(rootPath, "*.*")
.Sum(file => file.Length);
}
}
Code Quality
The library follows strict coding standards:
- 2-space indentation for C# files (enforced by
.editorconfig) - Comprehensive XML documentation for all public APIs
- Copyright headers in all files:
// Copyright ยฉ 2013-2025, EPSITEC SA, CH-1400 Yverdon-les-Bains, Switzerland - Thread-safe implementations where applicable (Repository, Singleton, etc.)
- IDisposable patterns with proper cleanup and reference counting
- Consistent naming following .NET conventions
- Immutability where appropriate (records, readonly fields)
Performance Considerations
- Repository<TKey, TValue>: Uses
ConcurrentDictionarywithLazy<T>for thread-safe lazy initialization - Option<T>: Implemented as record types for structural equality and minimal overhead
- Traverse: Non-recursive graph traversal avoids stack overflow with large hierarchies
- Memoization: Thread-safe caching with
ConcurrentDictionary - Task Extensions: Zero-allocation where possible using ValueTask
- StringExtensions: Optimized for minimal allocations
License
This project is licensed under the MIT License.
Repository
- Git Repository: Bcx on git.epsitec.ch
- Branch: master
- Development: https://git.epsitec.ch/libraries/bcx-dev
Contributing
Contributions are welcome! Please ensure:
- Code follows existing style and conventions (2-space indentation for C#)
- All tests pass
- XML documentation is provided for public APIs
- Copyright headers are included:
// Copyright ยฉ 2013-2025, EPSITEC SA, CH-1400 Yverdon-les-Bains, Switzerland - Code is thread-safe where appropriate
- IDisposable resources are properly managed with reference counting patterns
Support
For issues, questions, or contributions, please visit the project repository at git.epsitec.ch.
Made with โค๏ธ by EPSITEC SA
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.CSharp (>= 4.7.0)
- System.Buffers (>= 4.6.1)
- System.Collections.Immutable (>= 10.0.11)
- System.Interactive (>= 7.0.1)
- System.IO.Pipelines (>= 10.0.11)
- System.Memory (>= 4.6.3)
- System.Numerics.Vectors (>= 4.6.1)
- System.Reactive (>= 7.0.0)
- System.Text.Encodings.Web (>= 10.0.11)
- System.Text.Json (>= 10.0.11)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Epsitec.Bcx.Core:
| Package | Downloads |
|---|---|
|
Epsitec.Passport.Shared
Passport.Shared - 3.46.4.2634 net10.0 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 7.4.5.2641 | 115 | 10/7/2026 |
| 7.3.3.2636 | 472 | 9/5/2026 |
| 7.3.2.2636 | 103 | 9/5/2026 |
| 7.3.1.2636 | 102 | 9/5/2026 |
| 7.3.0.2636 | 115 | 9/5/2026 |
| 7.1.5.2635 | 200 | 8/27/2026 |
| 7.1.3.2633 | 195 | 8/15/2026 |
| 6.9.2.2623 | 201 | 6/5/2026 |
| 5.8.7.2551 | 352 | 12/16/2025 |
| 5.8.6.2551 | 325 | 12/16/2025 |
| 5.1.0.2423 | 302 | 6/11/2024 |
| 5.0.0.2418 | 231 | 6/6/2024 |