Table of Contents

CL.Common — Utilities

Strings, time, web, networking, cron, imaging, caching, file handling, and reflection.

This page covers the everyday utility helpers in CL.Common. Most are static classes — the one exception is the cache, which is an instance type (MemoryCache implementing ICache). Pure transforms and predicates return plain values; anything involving I/O, parsing, or the network returns a framework Result / Result<T>.

See the Overview for loading and the full catalog, and Security & Data for encryption, hashing, generators, JSON, conversion, and compression.

Strings — StringHelper

CL.Common.Strings.StringHelper is a kit of text transforms. Every method returns a plain value.

using CL.Common.Strings;

string slug  = StringHelper.ToSlug("Hello, World!");        // "hello-world"
string camel = StringHelper.ToCamelCase("hello world");     // "helloWorld"
string pascal= StringHelper.ToPascalCase("hello world");    // "HelloWorld"
string snake = StringHelper.ToSnakeCase("HelloWorld");      // "hello_world"
string kebab = StringHelper.ToKebabCase("HelloWorld");      // "hello-world"

string clip  = StringHelper.Truncate("a long sentence", 6); // "a long..."
string plain = StringHelper.StripHtml("<b>hi</b>");          // "hi"
int    words = StringHelper.WordCount("one two three");      // 3
Member Returns Purpose
Truncate(value, maxLength, suffix = "...") string Clip with an ellipsis (or custom suffix).
ToSlug / ToCamelCase / ToPascalCase / ToSnakeCase / ToKebabCase(value) string Case / slug conversions.
StripHtml(value) string Remove HTML tags.
WordCount(value) int Count words.
Repeat(value, count) string Repeat a string.
Reverse(value) string Reverse the characters.
ContainsAny(value, params values) bool Whether any candidate appears.
CountOccurrences(text, substring) int Count non-overlapping occurrences.
ExtractNumbers(text) IEnumerable<long> Pull integer runs out of text.
IsAscii(value) bool Whether the string is pure ASCII.
Capitalize(value) string Capitalize the first letter.

Strings — StringValidator

CL.Common.Strings.StringValidator is a set of bool predicates for common formats.

using CL.Common.Strings;

bool email = StringValidator.IsEmail("a@b.com");
bool url   = StringValidator.IsUrl("https://example.com");
bool ip4   = StringValidator.IsIPv4("192.168.0.1");
bool guid  = StringValidator.IsGuid("d3b07384-d9a0-4c9b-8b3e-...");
bool fits  = StringValidator.HasMaxLength("hi", 10);
Member Returns Purpose
IsEmail / IsUrl / IsPhoneNumber / IsNumeric / IsAlphanumeric / IsIPv4 / IsIPv6 / IsGuid(value) bool Format checks.
HasMinLength(value, min) / HasMaxLength(value, max) bool Length bounds.
MatchesPattern(value, pattern) bool Regex match.
IsNullOrWhiteSpace(value) / IsNotEmpty(value) bool Emptiness checks.

Time — DateTimeHelper

CL.Common.Time.DateTimeHelper handles Unix time, ISO 8601, business-day math, and boundary calculations. Almost all return plain values; FromIso8601 returns a Result because parsing can fail.

using CL.Common.Time;

long     unix = DateTimeHelper.ToUnixTimestamp(DateTime.UtcNow);
DateTime dt   = DateTimeHelper.FromUnixTimestamp(unix);              // UTC
string   iso  = DateTimeHelper.ToIso8601(DateTime.UtcNow);
Result<DateTime> parsed = DateTimeHelper.FromIso8601("2026-06-20T12:00:00Z");

int      age  = DateTimeHelper.GetAge(new DateTime(1990, 1, 1));
bool     wknd = DateTimeHelper.IsWeekend(DateTime.UtcNow);
DateTime nbd  = DateTimeHelper.NextBusinessDay(DateTime.UtcNow);
DateTime som  = DateTimeHelper.StartOfMonth(DateTime.UtcNow);
string   rel  = DateTimeHelper.ToRelativeString(DateTime.UtcNow.AddMinutes(-3)); // "3 minutes ago"
Member Returns Purpose
ToUnixTimestamp(value) long Seconds since epoch.
FromUnixTimestamp(seconds) DateTime UTC time from epoch seconds.
ToUnixMilliseconds(value) long Milliseconds since epoch.
ToIso8601(value) string ISO 8601 string.
FromIso8601(value) Result<DateTime> Parse ISO 8601.
GetAge(birthDate) int Whole years.
IsWeekend(value) / IsBusinessDay(value) bool Day classification.
NextBusinessDay(value) DateTime Next non-weekend day.
StartOfDay / EndOfDay / StartOfMonth / EndOfMonth(value) DateTime Period boundaries.
StartOfWeek(value, startOfWeek = Monday) DateTime Week start.
ToRelativeString(value) string Human "3 minutes ago" text.

Web

The CL.Common.Web namespace bundles four helpers. HttpClientHelper returns Result (network calls fail); the rest return plain values.

HtmlHelper

using CL.Common.Web;

string text = HtmlHelper.StripTags("<p>Hello <b>world</b></p>");   // "Hello world"
string safe = HtmlHelper.Sanitize(userHtml);
string enc  = HtmlHelper.Encode("<a>");                            // "&lt;a&gt;"
string clip = HtmlHelper.TruncateText(longHtml, 120);
Member Returns Purpose
StripTags / Sanitize / TrimWhitespace / Encode / Decode(html) string HTML cleanup and encoding.
TruncateText(html, maxLength, suffix = "...") string Length-limit HTML text.

HttpClientHelper

Typed Result-based requests over an HttpClient you supply, with optional headers.

using CL.Common.Web;

using var http = new HttpClient();

Result<string> body = await HttpClientHelper.GetStringAsync(http, "https://example.com");
Result<MyDto>  dto  = await HttpClientHelper.GetJsonAsync<MyDto>(http, "https://api.example.com/item/1");
Result<string> post = await HttpClientHelper.PostJsonAsync(http, url, new { name = "Ada" });
Result<MyDto>  rt   = await HttpClientHelper.PostJsonAsync<CreateDto, MyDto>(http, url, payload);
Result         del  = await HttpClientHelper.DeleteAsync(http, url);
Member Returns Purpose
GetStringAsync(client, url, headers?) Task<Result<string>> GET as text.
GetJsonAsync<T>(client, url, headers?) Task<Result<T>> GET + deserialize.
PostJsonAsync<TBody>(client, url, body, headers?) Task<Result<string>> POST JSON, raw response.
PostJsonAsync<TBody, TResponse>(client, url, body, headers?) Task<Result<TResponse>> POST JSON, typed response.
PutJsonAsync<TBody>(client, url, body, headers?) Task<Result<string>> PUT JSON.
DeleteAsync(client, url, headers?) Task<Result> DELETE.

HttpHeaderHelper

Pulls request-context facts out of raw header values.

using CL.Common.Web;

string locale = HttpHeaderHelper.GetPrimaryLocale(acceptLanguageHeader);   // e.g. "en"
string ip     = HttpHeaderHelper.GetEffectiveClientIp(remoteIp, xForwardedFor);
bool   bot    = HttpHeaderHelper.IsBot(userAgent);
bool   mobile = HttpHeaderHelper.IsMobile(userAgent);
bool   json   = HttpHeaderHelper.AcceptsJson(acceptHeader);
Member Returns Purpose
GetPrimaryLocale(acceptLanguage?) string Best language from Accept-Language.
GetEffectiveClientIp(remoteIp?, xForwardedFor?) string Real client IP behind a proxy.
IsPrivateIp(ip) bool Whether an IP is in a private range.
IsBot(userAgent?) / IsMobile(userAgent?) / AcceptsJson(accept?) bool User-agent / accept checks.
GetHeader(headers, name) string? Case-insensitive header lookup.

UrlHelper

using CL.Common.Web;

string url = UrlHelper.AppendQuery("https://api.example.com/search",
    new Dictionary<string, string> { ["q"] = "cats", ["page"] = "2" });

Dictionary<string, string> q = UrlHelper.ParseQuery(url);
string domain = UrlHelper.GetDomain(url);
bool   secure = UrlHelper.IsHttps(url);
Member Returns Purpose
Encode(value) / Decode(value) string URL encode/decode.
IsValid(url) bool Whether the string is a valid URL.
Combine(baseUrl, relativePath) string Join base + path.
AppendQuery(url, params) string Append query parameters.
ParseQuery(url) Dictionary<string, string> Parse the query string.
GetDomain(url) / GetPath(url) string Extract parts.
IsHttps(url) bool Whether the scheme is HTTPS.

Networking

CL.Common.Networking provides ping, DNS, subnet math, and traceroute as static helpers. Each returns a Result carrying a record.

using CL.Common.Networking;

Result<PingResult>     ping = await NetworkPing.PingAsync("example.com");
Result<string[]>       ips  = await NetworkDns.LookupAsync("example.com");
Result<SubnetInfo>     net  = SubnetCalculator.Calculate("192.168.1.10", "255.255.255.0");
Result<List<HopInfo>>  hops = await TraceRoute.TraceAsync("example.com");

if (ping.IsSuccess)
    Console.WriteLine($"{ping.Value.Host}: {ping.Value.RoundTripMs} ms");
Member Returns Purpose
NetworkPing.PingAsync(host, timeout = 3000) Task<Result<PingResult>> ICMP ping.
NetworkDns.LookupAsync(hostname) Task<Result<string[]>> Resolve a host to addresses.
SubnetCalculator.Calculate(ip, subnetMask) Result<SubnetInfo> IPv4 subnet math.
TraceRoute.TraceAsync(host, maxHops = 30) Task<Result<List<HopInfo>>> Walk the route to a host.
public record PingResult(bool Success, long RoundTripMs, string Host);
public record SubnetInfo(string NetworkAddress, string BroadcastAddress, string SubnetMask, int UsableHosts);
public record HopInfo(int Hop, string Address, long Ms);

Parser — Cron

CL.Common.Parser.CronParser parses 5-field cron expressions and computes the next UTC fire time. Parse and GetNextOccurrence return Result; IsValid returns bool.

using CL.Common.Parser;

bool good = CronParser.IsValid("0 */6 * * *");
Result<CronExpression> parsed = CronParser.Parse("0 */6 * * *");
Result<DateTime> next = CronParser.GetNextOccurrence("0 */6 * * *");        // from now (UTC)
Result<DateTime> from = CronParser.GetNextOccurrence("0 0 * * *", DateTime.UtcNow.AddDays(1));
Member Returns Purpose
Parse(expression) Result<CronExpression> Parse into fields.
IsValid(expression) bool Quick validity check.
GetNextOccurrence(expression, from? = null) Result<DateTime> Next UTC fire time at/after from.
public record CronExpression(string Raw, string Minutes, string Hours,
                             string DayOfMonth, string Month, string DayOfWeek);

Imaging — CLU_Imaging

CL.Common.Imaging.CLU_Imaging does image validation and transformation via SkiaSharp, working with both Streams and file paths. The validation predicates return bool; transforms return Result.

using CL.Common.Imaging;

// Validate
using var input = File.OpenRead("photo.jpg");
bool fits = CLU_Imaging.IsValidSize(input, maxWidth: 4000, maxHeight: 4000);

// Convert to WebP (stream in, stream out)
using var src = File.OpenRead("photo.jpg");
Result<MemoryStream> webp = CLU_Imaging.ConvertImage(src, ImageFormat.Webp);
if (webp.IsSuccess)
    await File.WriteAllBytesAsync("photo.webp", webp.Value!.ToArray());

// File-path overloads
CLU_Imaging.ResizeImage("in.png", "out.png", 800, 600, maintainAspectRatio: true);
CLU_Imaging.CreateThumbnail("in.png", "thumb.png", thumbnailSize: 128);
Result<(int width, int height)> dims = CLU_Imaging.GetImageDimensions("in.png");
Member Returns Purpose
IsValidSize(stream, maxWidth, maxHeight) bool Whether dimensions are within bounds.
IsValidAspectRatio(stream) bool Aspect-ratio sanity check.
ConvertImage(stream, format) Result<MemoryStream> Re-encode to JPEG/PNG/WebP.
ResizeImage(stream, targetWidth, targetHeight, allowCrop = true) Result<MemoryStream> Resize a stream.
ImageToBase64(imagePath) Result<string> Read a file as a Base64 string.
ResizeImage(inputPath, outputPath, width, height, maintainAspectRatio = true) Result Resize file → file.
ConvertImageFormat(inputPath, outputPath, format) Result Convert file → file.
CreateThumbnail(inputPath, outputPath, thumbnailSize = 100) Result Square thumbnail.
GetImageDimensions(imagePath) Result<(int width, int height)> Read width/height.
public enum ImageFormat { Jpeg, Png, Webp }

Caching — ICache / MemoryCache

CL.Common.Caching.MemoryCache is the one instance type here: a sealed, thread-safe, in-process cache implementing ICache and IDisposable. A background loop evicts expired entries on the cleanup interval (default 60 seconds).

using CL.Common.Caching;

ICache cache = new MemoryCache();                                  // or new MemoryCache(TimeSpan.FromSeconds(30))

await cache.SetAsync("user:1", userObj, TimeSpan.FromMinutes(5));
User? cached = await cache.GetAsync<User>("user:1");               // null if missing/expired
bool  has    = await cache.ExistsAsync("user:1");

await cache.RemoveByPrefixAsync("user:");
int count = await cache.GetCountAsync();
await cache.ClearAsync();
Member Returns Purpose
SetAsync<T>(key, value, ttl? = null) Task Store with optional TTL.
GetAsync<T>(key) Task<T?> Read; null if absent or expired.
ExistsAsync(key) Task<bool> Membership check.
RemoveAsync(key) Task Remove one entry.
RemoveByPrefixAsync(prefix) Task Remove all entries with a key prefix.
ClearAsync() Task Drop everything.
GetCountAsync() Task<int> Current entry count.

The constructor is MemoryCache(TimeSpan? cleanupInterval = null); pass a value to tune how often the eviction loop runs. Dispose the cache to stop the loop.

File handling — FileSystem

CL.Common.FileHandling.FileSystem wraps file I/O so errors come back as Result rather than exceptions. The existence checks return plain bool.

using CL.Common.FileHandling;

Result<string> text = await FileSystem.ReadAllTextAsync("notes.txt");
Result         w    = await FileSystem.WriteAllTextAsync("notes.txt", "hello");
Result         cp   = await FileSystem.CopyAsync("a.txt", "b.txt", overwrite: true);
Result<string[]> files = await FileSystem.GetFilesAsync("logs", "*.log");
Result<long>   size = FileSystem.GetFileSizeBytes("a.txt");

bool exists = FileSystem.FileExists("a.txt");
Member Returns Purpose
ReadAllTextAsync(path) / WriteAllTextAsync(path, content) Task<Result<string>> / Task<Result> Text I/O.
ReadAllBytesAsync(path) / WriteAllBytesAsync(path, data) Task<Result<byte[]>> / Task<Result> Binary I/O.
CopyAsync(source, dest, overwrite = false) / MoveAsync(source, dest) Task<Result> Copy / move.
DeleteFile(path) / CreateDirectory(path) Result Delete file / create directory.
GetFilesAsync(directory, pattern = "*") Task<Result<string[]>> List matching files.
GetFileSizeBytes(path) Result<long> File size in bytes.
FileExists(path) / DirectoryExists(path) bool Existence checks.

Reflection — AssemblyHelper & ReflectionHelper

The CL.Common.Assemblies namespace holds two static helpers. AssemblyHelper reads assembly metadata and discovers types; ReflectionHelper does dynamic instance and member access. Metadata getters return plain values; loading, discovery, and dynamic access return Result.

using CL.Common.Assemblies;

// Assembly metadata + discovery
string version = AssemblyHelper.GetVersion(someInstance);
Dictionary<string, string> meta = AssemblyHelper.GetMetadata(someInstance);
IEnumerable<Type> impls = AssemblyHelper.GetImplementors<IPlugin>(asm);
Result<string> res = AssemblyHelper.ReadEmbeddedResource(asm, "MyApp.banner.txt");

// Dynamic instance / member access
Result<MyType> made = ReflectionHelper.CreateInstance<MyType>();
Result<object?> val = ReflectionHelper.GetPropertyValue(obj, "Name");
Dictionary<string, object?> dict = ReflectionHelper.ToDictionary(obj);
bool plugin = ReflectionHelper.Implements<IPlugin>(typeof(MyType));

AssemblyHelper

Member Returns Purpose
GetName / GetVersion / GetDescription / GetTitle / GetCompany / GetProduct / GetInformationalVersion(instance) string Assembly attribute values.
GetMetadata(instance) Dictionary<string, string> All metadata at once.
LoadFrom(filePath) Result<Assembly> Load an assembly from disk.
GetFileMetadata(filePath) Result<Dictionary<string, string>> Metadata without loading into the AppDomain.
GetImplementors<T>(assembly) IEnumerable<Type> Types implementing T.
GetTypesWithAttribute<TAttr>(assembly) IEnumerable<Type> Types decorated with an attribute.
ReadEmbeddedResource(assembly, name) Result<string> Read an embedded text resource.

ReflectionHelper

Member Returns Purpose
CreateInstance<T>() Result<T> Construct via default constructor.
CreateInstance(type, params args) Result<object> Construct with arguments.
GetPropertyValue(obj, name) / SetPropertyValue(obj, name, value) Result<object?> / Result Read/write a property by name.
ToDictionary<T>(value) Dictionary<string, object?> Object → property map.
InvokeMethod(obj, name, params args) Result<object?> Call a method by name.
Implements<TInterface>(type) bool Interface check.
HasDefaultConstructor(type) bool Whether a no-arg constructor exists.
GetFriendlyName(type) string Readable type name (with generics).

See also