API Reference
This page lists all 45 public types and 610 public members of the package, grouped by the type that declares them. Each row shows the member as it is declared in C#, the summary from its XML documentation, and a link to the guide section that explains it with examples.
Extension classes are declared in the namespace of the type they extend (System, System.Collections.Generic, System.Linq, ...), so their methods show up without an extra using. A signature that starts with extension(...) is a C# 14 extension member; see C# 14 extension members.
This page is generated from the XML documentation and the public API baseline (PublicAPI.Unshipped.txt) by website/scripts/generate-api-reference.mjs. Do not edit it by hand. To regenerate it, run from the repository root:
dotnet build src/Masterly.Extensions.Core -c Release -f net10.0
node website/scripts/generate-api-reference.mjs src/Masterly.Extensions.Core/bin/Release/net10.0/Masterly.Extensions.Core.xml
AsyncExtensions
Extension methods for Task.
Namespace: System.Threading.Tasks.
| Member | Summary | Guide |
|---|---|---|
Task<T> Catch<TException, T>(this Task<T> task, Func<TException, T> handler) | Recovers from a failure of type TException (or a derived type) with a replacement result: int count = await CountAsync().Catch<TimeoutException, int>(ex => -1). Other exceptions propagate unchanged. Cancellation counts as a failure only when TException is OperationCanceledException or one of its base types. | Tasks and Resilience |
Task Catch<TException>(this Task task, Action<TException> handler) | Handles a failure of type TException (or a derived type) and completes normally: await SendAsync().Catch<HttpRequestException>(ex => logger.LogWarning(ex, "Send failed")). Other exceptions propagate unchanged. Cancellation counts as a failure only when TException is OperationCanceledException or one of its base types. | Tasks and Resilience |
Task<T> FirstSuccessful<T>(this IEnumerable<Task<T>> tasks) | Returns the result of whichever task succeeds first — for hedged requests to several mirrors or replicas. Failed tasks are skipped; the others keep running (cancel them yourself if needed). | Tasks and Resilience |
void Forget(this Task task, Action<Exception> onError) | Starts a task without awaiting it, and reports its failure to onError instead of losing it. Use for background work whose result you do not need, such as sending telemetry. | Tasks and Resilience |
IEnumerable<Task<T>> InCompletionOrder<T>(this IEnumerable<Task<T>> tasks) | Returns tasks that complete in the order the inputs finish, so results can be processed as they arrive instead of waiting for the slowest one. Each returned task completes like the input that finished in that position: with its result, its exception (unwrapped) or its cancellation. | Tasks and Resilience |
Task Then(this Task task, Action next) | Runs an action once the task succeeds: await ImportAsync().Then(() => logger.LogInformation("Done")). | Tasks and Resilience |
Task Then(this Task task, Func<Task> next) | Continues with another async step once the task succeeds: await ImportAsync().Then(ReindexAsync). | Tasks and Resilience |
Task<TResult> Then<T, TResult>(this Task<T> task, Func<T, Task<TResult>> next) | Continues with another async step once the task succeeds, and flattens the result: await GetUserAsync(id).Then(user => GetOrdersAsync(user)) is a Task<Order[]>, not a Task<Task<Order[]>>. | Tasks and Resilience |
Task<TResult> Then<T, TResult>(this Task<T> task, Func<T, TResult> next) | Maps the task's result once it succeeds: await LoadAsync().Then(Parse). | Tasks and Resilience |
Task Then<T>(this Task<T> task, Action<T> next) | Runs an action with the task's result once it succeeds: await GetUserAsync(id).Then(Cache). | Tasks and Resilience |
Task Then<T>(this Task<T> task, Func<T, Task> next) | Continues with another async step once the task succeeds: await GetUserAsync(id).Then(SaveAsync). | Tasks and Resilience |
Task<TResult> Then<TResult>(this Task task, Func<Task<TResult>> next) | Continues with an async step that produces a value once the task succeeds, and flattens the result: Report report = await ImportAsync().Then(BuildReportAsync). | Tasks and Resilience |
Task<TResult> Then<TResult>(this Task task, Func<TResult> next) | Produces a value once the task succeeds: int count = await ImportAsync().Then(() => importer.Count). | Tasks and Resilience |
Task<TimeSpan> Timed(this Task task) | Awaits the task and reports how long the wait took. | Tasks and Resilience |
Task<(T Result, TimeSpan Elapsed)> Timed<T>(this Task<T> task) | Awaits the task and also reports how long the wait took. Call it right where the task starts so the time covers the whole operation. | Tasks and Resilience |
Task<IReadOnlyList<TaskOutcome<T>>> WhenAllSettled<T>(this IEnumerable<Task<T>> tasks) | Waits for every task and reports how each one ended, without throwing — like JavaScript's Promise.allSettled. Use it when some failures are acceptable, such as a batch of uploads. | Tasks and Resilience |
Task<T> WithFallback<T>(this Task<T> task, T fallback) | Returns fallback instead of failing when the task throws. | Tasks and Resilience |
Task<T> WithFallback<T>(this Task<T> task, T fallback, TimeSpan timeout) | Returns fallback instead of failing when the task throws or takes longer than timeout — keep a page fast when a non-essential service is slow. | Tasks and Resilience |
Task<T> WithFallback<T>(this Task<T> task, T fallback, TimeSpan timeout, Action<Exception>? onFallback) | Returns fallback instead of failing when the task throws or takes longer than timeout, and reports why to onFallback — so failures are hidden from the user but not from your logs. | Tasks and Resilience |
Task WithTimeout(this Task task, TimeSpan timeout) | Waits for the task, but fails with TimeoutException if it takes longer than timeout. The task itself keeps running; only the wait is abandoned. | Tasks and Resilience |
Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout) | Waits for the task, but fails with TimeoutException if it takes longer than timeout. The task itself keeps running; only the wait is abandoned. | Tasks and Resilience |
Task WithTimeout(this Task task, TimeSpan timeout, CancellationToken cancellationToken) | Waits for the task, but fails with TimeoutException if it takes longer than timeout. The task itself keeps running; only the wait is abandoned. | Tasks and Resilience |
Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout, CancellationToken cancellationToken) | Waits for the task, but fails with TimeoutException if it takes longer than timeout. The task itself keeps running; only the wait is abandoned. | Tasks and Resilience |
AwaitableExtensions
Makes everyday values awaitable. await works on anything with a GetAwaiter() method, and an extension method counts, so these lines compile.
Namespace: System.Threading.Tasks.
| Member | Summary | Guide |
|---|---|---|
IAsyncEnumerable<long> Every(this TimeSpan interval) | A periodic timer as a sequence: yields 1, 2, 3, ... each time the interval elapses, forever. | Await Anything |
IAsyncEnumerable<long> Every(this TimeSpan interval, CancellationToken cancellationToken) | A periodic timer as a sequence: yields 1, 2, 3, ... each time the interval elapses, and ends quietly (without throwing) once cancellationToken is cancelled — made for the main loop of a BackgroundService. On .NET 6+ the ticks do not drift; a slow loop body skips ticks rather than catching up. | Await Anything |
IAsyncEnumerator<long> GetAsyncEnumerator(this TimeSpan interval) | Makes a TimeSpan a periodic timer for await foreach: each time the interval elapses the loop body runs with the tick number (1, 2, 3, ...), forever — break to stop. To stop on a CancellationToken instead, use Every. | Await Anything |
TaskAwaiter GetAwaiter(this IEnumerable<Task> tasks) | Awaits every task in the sequence at once: await files.Select(UploadAsync);. | Await Anything |
TaskAwaiter GetAwaiter(this Lazy<Task> lazyTask) | Awaits a lazily created task; the task is created (and so started) by the first await and shared by every later one: await lazyWarmUp;. | Await Anything |
TaskAwaiter GetAwaiter(this CancellationToken cancellationToken) | Awaits until cancellation is requested on the token. Completes normally (it does not throw), which makes it handy for "run until shutdown" loops: await stoppingToken;. A token that can never be cancelled (such as CancellationToken.None) never completes. | Await Anything |
TaskAwaiter<IDisposable> GetAwaiter(this SemaphoreSlim semaphore) | Awaits the semaphore as a lock: using (await _lock) { ... } is using (await _lock.LockAsync()) { ... }. The handle releases the semaphore when disposed, once, even if disposed twice. | Await Anything |
TaskAwaiter GetAwaiter(this WaitHandle handle) | Awaits a WaitHandle without blocking a thread: await readyEvent;. Works with handles that have no thread affinity, such as ManualResetEvent, AutoResetEvent and Semaphore. Waiting on an AutoResetEvent or a Semaphore consumes the signal or a count. Uses ThreadPool.RegisterWaitForSingleObject, and the registration is always unregistered once the handle is signaled. A Mutex is rejected: it would be acquired by a thread-pool thread, so the awaiting code could never release it. For async mutual exclusion use SemaphoreSlim, for example using (await semaphore.LockAsync()) { ... }. | Await Anything |
TaskAwaiter GetAwaiter(this TimeSpan delay) | Awaits a delay: await 500.Milliseconds(); is await Task.Delay(500). | Await Anything |
TaskAwaiter<List<T>> GetAwaiter<T>(this IAsyncEnumerable<T> source) | Awaits an async stream as a list: List<Order> all = await GetOrdersAsync(); reads every item. There is no cancellation here; to cancel, enumerate instead: await foreach (Order order in GetOrdersAsync().WithCancellation(token)). | Await Anything |
TaskAwaiter<T[]> GetAwaiter<T>(this IEnumerable<Task<T>> tasks) | Awaits every task in the sequence at once and returns their results in the same order. string[] pages = await urls.Select(FetchAsync); is await Task.WhenAll(urls.Select(FetchAsync)). | Await Anything |
TaskAwaiter<T> GetAwaiter<T>(this Lazy<Task<T>> lazyTask) | Awaits a lazily created task; the task is created (and so started) by the first await and shared by every later one — the classic "async lazy". | Await Anything |
ByteArrayExtensions
Extension methods for byte arrays.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
string ToBase64Url(this byte[] bytes) | Encodes the bytes as Base64url: URL- and file-name-safe Base64 without padding (RFC 4648 §5), as used in JWTs and tokens in links. Reverse it with FromBase64Url(). | Encoding and Validation |
string ToHex(this byte[] bytes) | Encodes the bytes as lower-case hex: new byte[] { 255, 0 } → "ff00". Reverse it with FromHex(). | Encoding and Validation |
CollectionDiff<T>
The result of DiffWith: how a collection changed between two versions.
Namespace: System.Collections.Generic. Guide: Diff and Dependencies.
| Member | Summary | Guide |
|---|---|---|
IReadOnlyList<T> Added { get; } | Gets the items whose key exists only in the new version, in new-version order. | Diff and Dependencies |
IReadOnlyList<(T Old, T New)> Changed { get; } | Gets the items present in both versions whose values differ, as (old, new) pairs. | Diff and Dependencies |
bool HasChanges { get; } | Gets a value indicating whether anything was added, removed or changed. | Diff and Dependencies |
IReadOnlyList<T> Removed { get; } | Gets the items whose key exists only in the old version, in old-version order. | Diff and Dependencies |
IReadOnlyList<(T Old, T New)> Unchanged { get; } | Gets the items present in both versions with equal values, as (old, new) pairs. | Diff and Dependencies |
CollectionExtensions
Extension methods for ICollection.
Namespace: System.Collections.Generic.
| Member | Summary | Guide |
|---|---|---|
IEnumerable<T> AddIfNotContains<T>(this ICollection<T> source, IEnumerable<T> items) | Adds each item the collection does not already contain. | Lists and Collections |
bool AddIfNotContains<T>(this ICollection<T> source, T item) | Adds the item if the collection does not already contain it. | Lists and Collections |
bool AddIfNotContains<T>(this ICollection<T> source, Func<T, bool> predicate, Func<T> itemFactory) | Adds an item created by itemFactory if no item matches predicate. | Lists and Collections |
bool IsNullOrEmpty<T>(this ICollection<T>? source) | Checks whether the collection is null or has no items. | Lists and Collections |
void RemoveAll<T>(this ICollection<T> source, IEnumerable<T> items) | Removes each of the given items from the collection. | Lists and Collections |
IList<T> RemoveAll<T>(this ICollection<T> source, Func<T, bool> predicate) | Removes every item that matches predicate. | Lists and Collections |
CollectionInitializerExtensions
Add extension methods that make C# collection-initializer syntax (new T { a, b }) work on collections that have no public Add, and let initializers take tuples or whole sequences. The compiler binds each initializer element to any accessible Add, including these.
Namespace: System.Collections.Generic.
| Member | Summary | Guide |
|---|---|---|
void Add<T>(this ConcurrentQueue<T> queue, T item) | Enqueues the item, so new ConcurrentQueue<T> { a, b, c } compiles. | Collection Initializers |
void Add<T>(this ConcurrentStack<T> stack, T item) | Pushes the item, so new ConcurrentStack<T> { a, b, c } compiles and leaves c on top. | Collection Initializers |
void Add<T>(this ICollection<T> collection, IEnumerable<T> items) | Appends every item of items — a "spread" for initializers: new List<int> { 1, 2, otherList } adds each item of otherList. The instance Add(T) always wins when the element is itself a T, so new List<object> { someList } still adds someList as one item. | Collection Initializers |
void Add<T>(this LinkedList<T> list, T item) | Appends the item, so new LinkedList<T> { a, b, c } compiles (LinkedList implements ICollection<T>.Add only explicitly). | Collection Initializers |
void Add<T>(this Queue<T> queue, T item) | Enqueues the item, so new Queue<T> { a, b, c } compiles and dequeues a first. | Collection Initializers |
void Add<T>(this Stack<T> stack, T item) | Pushes the item, so new Stack<T> { a, b, c } compiles and leaves c on top. | Collection Initializers |
void Add<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, (TKey Key, TValue Value) entry) | Adds a (key, value) tuple, so new Dictionary<string, int> { ("a", 1), ("b", 2) } compiles. A duplicate key throws, exactly like Add(key, value). | Collection Initializers |
ComparableExtensions
Extension methods for IComparable.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
T Clamp<T>(this T value, T min, T max) | Limits the value to the range [min, max]. Works for any comparable type: numbers, dates, strings, versions, ... | Numbers |
bool Includes<T>(this (T Start, T End) range, T value) | Checks whether the value falls in the half-open range [Start, End): the start is included, the end is not. | Numbers |
bool IsBetween<T>(this T value, T minInclusiveValue, T maxInclusiveValue) | Checks whether the value is between minInclusiveValue and maxInclusiveValue. | Numbers |
bool Overlaps<T>(this (T Start, T End) range, (T Start, T End) other) | Checks whether two ranges share any part. Ranges are half-open — the end is not included — so a booking that ends at 14:00 does not overlap one that starts at 14:00, and an empty range (start = end) overlaps nothing. Works on tuples of any comparable type. | Numbers |
CultureInfoExtensions
Extension methods for CultureInfo.
Namespace: System.Globalization.
| Member | Summary | Guide |
|---|---|---|
IDisposable Use(this CultureInfo culture) | Makes this culture the current culture and UI culture until the returned handle is disposed, then restores the previous ones. Handy for formatting in a specific language, and for tests. | IO and System |
CyclicDependencyException
Thrown by TopologicalSort when the dependencies form a cycle.
Namespace: System.Collections.Generic. Guide: Diff and Dependencies.
| Member | Summary | Guide |
|---|---|---|
CyclicDependencyException(IReadOnlyList<object> cycle) | Initializes a new instance of the CyclicDependencyException class. | Diff and Dependencies |
IReadOnlyList<object> Cycle { get; } | Gets the items in the cycle, in dependency order. The first item is repeated at the end. | Diff and Dependencies |
DateTimeExtensions
Calendar helpers for DateTime: quarters, same-period checks, business-day counting, Unix time and "the second Tuesday of the month".
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
DateTime AddBusinessDays(this DateTime date, int days) | Adds working days (Monday to Friday), skipping weekends. Negative values go back in time. Public holidays are not known and are counted as working days. | Dates |
int Age(this DateTime birthDate, DateTime today) | Computes the age in whole years on today. A Feb 29 birthday counts on Feb 28 in other years. | Dates |
int BusinessDaysUntil(this DateTime start, DateTime end) | Counts the working days (Monday to Friday) after start up to and including end; negative when end is earlier (a.BusinessDaysUntil(b) == -b.BusinessDaysUntil(a)). Only the date parts are used. Public holidays are not known and are counted as working days. This is the inverse of AddBusinessDays: for a weekday start, start.BusinessDaysUntil(start.AddBusinessDays(n)) == n. | Dates |
DateTime CeilingTo(this DateTime value, TimeSpan interval) | Rounds up to the next multiple of interval (or returns the value when it is already a multiple). The DateTime.Kind is kept. Multiples are counted from DateTime.MinValue, so intervals that divide a day evenly align with the clock. | Dates |
void Deconstruct(this DateTime date, out int year, out int month, out int day) | Deconstructs the date part: var (year, month, day) = date;. | foreach and Deconstruction |
void Deconstruct(this DateTime date, out int year, out int month, out int day, out int hour, out int minute, out int second) | Deconstructs the date and the time of day to the second: var (year, month, day, hour, minute, second) = moment;. Milliseconds and ticks are dropped. | foreach and Deconstruction |
DateTime EndOfDay(this DateTime date) | Gets the last tick of the day (23:59:59.9999999). | Dates |
DateTime EndOfMonth(this DateTime date) | Gets the last tick of the last day of the month. | Dates |
DateTime EndOfQuarter(this DateTime date) | Gets the last tick of the calendar quarter (March, June, September or December 31st/30th). | Dates |
DateTime EndOfWeek(this DateTime date, DayOfWeek firstDayOfWeek = DayOfWeek.Monday) | Gets the last tick of the week that contains the date. | Dates |
DateTime EndOfYear(this DateTime date) | Gets the last tick of December 31st. | Dates |
DateTime FloorTo(this DateTime value, TimeSpan interval) | Rounds down to the previous multiple of interval (or returns the value when it is already a multiple). The DateTime.Kind is kept. Multiples are counted from DateTime.MinValue, so intervals that divide a day evenly align with the clock. | Dates |
DateTime FromUnixTimeMilliseconds(this long milliseconds) | Converts milliseconds since the Unix epoch to a UTC DateTime: the number JavaScript's Date.now() gives. An int literal needs an L suffix or a cast. | Dates |
DateTime FromUnixTimeSeconds(this long seconds) | Converts seconds since the Unix epoch to a UTC DateTime. The receiver is a long, and C# does not widen the receiver of an extension method, so an int literal needs an L suffix or a cast: 1715774400L.FromUnixTimeSeconds(). | Dates |
bool IsSameDay(this DateTime date, DateTime other) | Checks whether two moments fall on the same calendar day. Only the date part is compared; the time of day and the DateTime.Kind are ignored, so convert both to the same zone first. | Dates |
bool IsSameMonth(this DateTime date, DateTime other) | Checks whether two moments fall in the same calendar month of the same year. The DateTime.Kind is ignored. | Dates |
bool IsSameWeek(this DateTime date, DateTime other) | Checks whether two moments fall in the same week, with weeks starting on Monday (ISO 8601). The DateTime.Kind is ignored. | Dates |
bool IsSameWeek(this DateTime date, DateTime other, DayOfWeek firstDayOfWeek) | Checks whether two moments fall in the same week, with weeks starting on firstDayOfWeek. The DateTime.Kind is ignored. | Dates |
bool IsWeekend(this DateTime date) | Checks whether the date falls on a Saturday or Sunday. | Dates |
DateTime LastWeekdayOfMonth(this DateTime date, DayOfWeek dayOfWeek) | Gets the last occurrence of a weekday in the date's month, at midnight: the last Friday, ... | Dates |
DateTime Next(this DateTime date, DayOfWeek dayOfWeek) | Gets the next date that falls on dayOfWeek, always after this date (a Friday's next Friday is a week later). The time of day is kept. | Dates |
DateTime? NthWeekdayOfMonth(this DateTime date, DayOfWeek dayOfWeek, int occurrence) | Gets the n-th occurrence of a weekday in the date's month, at midnight: the second Tuesday, the first Friday, ... Every month has four of each weekday; null is returned when the fifth does not exist. | Dates |
DateTime Previous(this DateTime date, DayOfWeek dayOfWeek) | Gets the previous date that fell on dayOfWeek, always before this date. The time of day is kept. | Dates |
int Quarter(this DateTime date) | Gets the calendar quarter, 1 to 4. | Dates |
DateTime RoundTo(this DateTime value, TimeSpan interval) | Rounds to the nearest multiple of interval; a midpoint rounds up. The DateTime.Kind is kept. | Dates |
DateTime StartOfDay(this DateTime date) | Gets midnight at the start of the day. | Dates |
DateTime StartOfMonth(this DateTime date) | Gets midnight on the first day of the month. | Dates |
DateTime StartOfQuarter(this DateTime date) | Gets midnight on the first day of the calendar quarter (January, April, July or October 1st). | Dates |
DateTime StartOfWeek(this DateTime date, DayOfWeek firstDayOfWeek = DayOfWeek.Monday) | Gets the start of the week that contains the date. | Dates |
DateTime StartOfYear(this DateTime date) | Gets midnight on January 1st. | Dates |
IEnumerable<DateTime> To(this DateTime start, DateTime end) | Walks from this moment to end one day at a time, keeping the start's time of day. Every step that is not after end is included. For whole calendar days, pass dates without a time (start.Date.To(end.Date)), which includes both ends. | foreach and Deconstruction |
IEnumerable<DateTime> To(this DateTime start, DateTime end, TimeSpan step) | Walks from this date to end in steps of step, both ends included when the steps land on end. | foreach and Deconstruction |
string ToRelativeTime(this DateTime value, DateTime reference) | Describes the date relative to reference in English: "just now", "5 minutes ago", "yesterday", "in 3 days", "2 years ago", ... | Dates |
long ToUnixTimeMilliseconds(this DateTime value) | Gets the milliseconds since the Unix epoch (1970-01-01 00:00:00 UTC). A DateTimeKind.Local value is converted to UTC first; an DateTimeKind.Unspecified value is treated as UTC (the machine's time zone is never used). Fractions of a millisecond are dropped. | Dates |
long ToUnixTimeSeconds(this DateTime value) | Gets the seconds since the Unix epoch (1970-01-01 00:00:00 UTC), as DateTimeOffset.ToUnixTimeSeconds does. A DateTimeKind.Local value is converted to UTC first; an DateTimeKind.Unspecified value is treated as UTC (the machine's time zone is never used). Fractions of a second are dropped; moments before 1970 give negative numbers. | Dates |
int WeekOfYear(this DateTime date) | Gets the ISO 8601 week number (weeks start on Monday; week 1 contains the first Thursday of the year). Around New Year the week can belong to the neighbouring year: 2027-01-01 is in week 53 of 2026. | Dates |
DictionaryExtensions
Extension methods for dictionaries.
Namespace: System.Collections.Generic.
| Member | Summary | Guide |
|---|---|---|
TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory) | Stores addValue when the key is missing, otherwise replaces the stored value with the result of updateValueFactory — the same contract as ConcurrentDictionary.AddOrUpdate, for plain dictionaries. | Dictionaries |
TValue? GetClosest<TValue>(this IEnumerable<KeyValuePair<string, TValue>> source, string key) | Gets the value under the key closest to key (case-insensitive): a typo-tolerant lookup for settings, headers and command names. An exact key wins without scoring. | Fuzzy and Natural Sort |
TValue? GetClosest<TValue>(this IEnumerable<KeyValuePair<string, TValue>> source, string key, double minSimilarity) | Gets the value under the key closest to key (case-insensitive). An exact key wins without scoring. | Fuzzy and Natural Sort |
string? GetClosestKey<TValue>(this IEnumerable<KeyValuePair<string, TValue>> source, string key) | Finds the key closest to key (case-insensitive), for "did you mean ...?" messages over settings, headers or command names. An exact key wins without scoring. Works on Dictionary, IDictionary, IReadOnlyDictionary and any sequence of pairs. | Fuzzy and Natural Sort |
string? GetClosestKey<TValue>(this IEnumerable<KeyValuePair<string, TValue>> source, string key, double minSimilarity) | Finds the key closest to key (case-insensitive). An exact key wins without scoring. | Fuzzy and Natural Sort |
TValue GetOrAdd<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> source, TKey key, Func<TValue> factory) | Gets the value for key, or creates it with factory, stores it and returns it. Thread-safe; under contention factory may run more than once, but only one value is stored. | Dictionaries |
TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, Func<TKey, TValue> factory) | Gets the value for key, or creates it with factory, stores it and returns it. | Dictionaries |
TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, Func<TValue> factory) | Gets the value for key, or creates it with factory, stores it and returns it. | Dictionaries |
TValue? GetOrDefault<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> source, TKey key) | Gets the value for key, or default if the key is missing. | Dictionaries |
TValue? GetOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key) | Gets the value for key, or default if the key is missing. | Dictionaries |
TValue? GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key) | Gets the value for key, or default if the key is missing. | Dictionaries |
TValue? GetOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> source, TKey key) | Gets the value for key, or default if the key is missing. | Dictionaries |
TValue GetOrDefault<TKey, TValue>(this ConcurrentDictionary<TKey, TValue> source, TKey key, TValue defaultValue) | Gets the value for key, or defaultValue if the key is missing. | Dictionaries |
TValue GetOrDefault<TKey, TValue>(this Dictionary<TKey, TValue> source, TKey key, TValue defaultValue) | Gets the value for key, or defaultValue if the key is missing. | Dictionaries |
TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, TValue defaultValue) | Gets the value for key, or defaultValue if the key is missing. | Dictionaries |
TValue GetOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> source, TKey key, TValue defaultValue) | Gets the value for key, or defaultValue if the key is missing. | Dictionaries |
int Increment<TKey>(this IDictionary<TKey, int> source, TKey key) | Adds 1 to the count stored under key; a missing key starts from 0. Only int counters are supported (an extension on IDictionary<TKey, long> would not be callable here). Not thread-safe, even on a ConcurrentDictionary. | Dictionaries |
int Increment<TKey>(this IDictionary<TKey, int> source, TKey key, int by) | Adds by to the count stored under key; a missing key starts from 0. Only int counters are supported. Not thread-safe, even on a ConcurrentDictionary. | Dictionaries |
Dictionary<TValue, TKey> Invert<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source) | Swaps keys and values: { "JO": "Jordan" } becomes { "Jordan": "JO" }. | Dictionaries |
Dictionary<TKey, TValue> Merge<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source, IEnumerable<KeyValuePair<TKey, TValue>> other) | Combines two dictionaries into a new one. When a key exists in both, the value from other wins — the usual "defaults, then overrides" pattern for settings. | Dictionaries |
Dictionary<TKey, TValue> Merge<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, TValue>> source, IEnumerable<KeyValuePair<TKey, TValue>> other, Func<TKey, TValue, TValue, TValue> resolveConflict) | Combines two dictionaries into a new one, deciding with resolveConflict what to keep when a key exists in both. | Dictionaries |
int RemoveWhere<TKey, TValue>(this IDictionary<TKey, TValue> source, Func<TKey, TValue, bool> predicate) | Removes every entry that matches predicate. | Dictionaries |
T ToObject<T>(this IDictionary<string, object?> source) | Creates a T and copies each entry to the public writable property with the same name. Values are converted to the property type when needed ("42" → int, "Monday" → DayOfWeek, ...). Keys that match no property are ignored. | Dictionaries |
string ToQueryString(this IDictionary<string, object?> source) | Builds a URL query string such as a=1&b=x%20y. Keys and values are percent-encoded, numbers use the invariant culture, DateTime / DateTimeOffset use the ISO 8601 round-trip format, DateOnly is written as yyyy-MM-dd and TimeOnly as HH:mm:ss.FFFFFFF. A null value is written as an empty value (key=). A collection value repeats the key (ids=1&ids=2) and skips null items. Values must be simple values or collections of simple values. IFormattable values use the invariant culture; any other value is written with its ToString(), which may depend on the current culture, and is not flattened (a byte[] is a collection, so each byte repeats the key). For nested objects use object.ToQueryString(), which writes Address.City=.... | Dictionaries |
bool TryGetValue<T>(this IDictionary<string, object?> source, string key, out T value) | Gets a value and casts it to T in one step. | Dictionaries |
extension<TKey, TValue>(IReadOnlyDictionary<TKey, TValue>) static Dictionary<TKey, TValue> operator +(IReadOnlyDictionary<TKey, TValue> first, IReadOnlyDictionary<TKey, TValue> second) | Combines two dictionaries into a new one; when a key exists in both, the right-hand value wins: Dictionary<string, string> settings = defaults + overrides;. The same as Merge. | C# 14 Extension Members |
DisposableExtensions
Extension methods for IDisposable and for turning clean-up code into one.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
IDisposable AsDisposable(this Action onDispose) | Turns an action into an IDisposable that runs it exactly once, on the first IDisposable.Dispose; later disposes do nothing. Thread-safe. C# cannot call a method on a lambda, so assign it to a delegate first. | Exceptions and Disposables |
void DisposeAll(this IEnumerable<IDisposable?> disposables) | Disposes every non-null item. A IDisposable.Dispose that throws does not stop the others from being disposed: when exactly one fails its exception is rethrown (with its stack trace) after the rest are disposed, and when several fail an AggregateException holds them all. | Exceptions and Disposables |
EnumerableExtensions
Extension methods for IEnumerable.
Namespace: System.Collections.Generic.
| Member | Summary | Guide |
|---|---|---|
bool AllIn<TSource>(this IEnumerable<TSource> source, params TSource[] target) | Checks whether every item of the sequence appears in target. | Querying Sequences |
bool AllIn<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> target, IEqualityComparer<TSource>? comparer) | Checks whether every item of the sequence appears in target. | Querying Sequences |
IEnumerable<(T1 First, T2 Second)> CartesianProduct<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> other) | Pairs every item of the sequence with every item of other. | Transforming Sequences |
IEnumerable<TResult> CartesianProduct<T1, T2, TResult>(this IEnumerable<T1> source, IEnumerable<T2> other, Func<T1, T2, TResult> resultSelector) | Combines every item of the sequence with every item of other. | Transforming Sequences |
string? ClosestMatch(this IEnumerable<string> candidates, string input) | Finds the candidate most similar to input (case-insensitive), for "did you mean ...?" messages. Uses Similarity. | Fuzzy and Natural Sort |
string? ClosestMatch(this IEnumerable<string> candidates, string input, double minSimilarity) | Finds the candidate most similar to input (case-insensitive). | Fuzzy and Natural Sort |
IEnumerable<IReadOnlyList<T>> Combinations<T>(this IEnumerable<T> source, int size) | Yields every subset of exactly size items, keeping the source order inside each subset. | Transforming Sequences |
bool ContainsAll<T>(this IEnumerable<T> source, params T[] target) | Checks whether the sequence contains every one of the target items. | Querying Sequences |
bool ContainsAll<T>(this IEnumerable<T> source, IEnumerable<T> target, IEqualityComparer<T>? comparer) | Checks whether the sequence contains every one of the target items. | Querying Sequences |
CollectionDiff<T> DiffWith<T, TKey>(this IEnumerable<T> oldItems, IEnumerable<T> newItems, Func<T, TKey> keySelector, IEqualityComparer<T>? valueComparer = null, IEqualityComparer<TKey>? keyComparer = null) | Compares an old and a new version of a collection by key and reports what was added, removed, changed and left unchanged. Useful for syncing a database, a UI list or a cache. | Diff and Dependencies |
IEnumerable<T> DistinctUntilChanged<T>(this IEnumerable<T> source) | Drops items equal to the item right before them: [1, 1, 2, 2, 2, 1] → [1, 2, 1]. | Transforming Sequences |
IEnumerable<T> DistinctUntilChanged<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, IEqualityComparer<TKey>? comparer = null) | Drops items whose key equals the key of the item right before them — for example status events where only changes matter. | Transforming Sequences |
IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> source) | Flattens one level of nesting: a sequence of sequences becomes one sequence. | Querying Sequences |
Dictionary<T, int> Frequencies<T>(this IEnumerable<T> source) | Counts how many times each item occurs. Keys keep first-seen order. | Querying Sequences |
Dictionary<TKey, int> Frequencies<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, IEqualityComparer<TKey>? comparer = null) | Counts how many items share each key. Keys keep first-seen order. | Querying Sequences |
IEnumerable<TResult> FullOuterJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter?, TInner?, TKey, TResult> resultSelector, IEqualityComparer<TKey>? comparer = null) | Joins two sequences by key and keeps every row of both sides: outer rows that match nothing get a default inner, inner rows that match nothing get a default outer. Keys may be null. | Querying Sequences |
IEnumerable<(TKey Key, IReadOnlyList<T> Items)> GroupAdjacent<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector) | Groups runs of consecutive items with the same key. Unlike GroupBy, a key that comes back later starts a new group, and the order is kept: [A, A, B, A] → (A: 2 items), (B: 1), (A: 1). | Transforming Sequences |
IEnumerable<T> InRandomOrder<T>(this IEnumerable<T> source, Random random) | Returns the items in a random order (Fisher–Yates on a copy; the source is not changed). The same seed gives the same order, which keeps tests deterministic. | Querying Sequences |
IEnumerable<T> Interleave<T>(this IEnumerable<T> source, params IEnumerable<T>[] others) | Merges sequences round-robin: the first item of each, then the second of each, and so on. A sequence that runs out simply drops out; the others continue. | Querying Sequences |
bool IsEmpty<TSource>(this IEnumerable<TSource> source) | Checks whether the sequence has no items. | Querying Sequences |
bool IsSorted<T>(this IEnumerable<T> source) | Checks whether the items are in non-decreasing order. Empty and single-item sequences are sorted. | Querying Sequences |
bool IsSorted<T>(this IEnumerable<T> source, IComparer<T>? comparer) | Checks whether the items are in non-decreasing order under comparer. Empty and single-item sequences are sorted. | Querying Sequences |
bool IsSortedBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector) | Checks whether the items are in non-decreasing order of keySelector. Empty and single-item sequences are sorted. | Querying Sequences |
bool IsSortedBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, IComparer<TKey>? comparer) | Checks whether the items are in non-decreasing order of keySelector under comparer. Empty and single-item sequences are sorted. | Querying Sequences |
IEnumerable<T> Iterate<T>(this T seed, Func<T, T> next) | Produces an endless sequence: the seed, then next applied again and again. Combine with Take or TakeWhile. | Transforming Sequences |
string JoinWith<T>(this IEnumerable<T> source, char separator) | Joins the items into one string with separator between them. Values are formatted with the invariant culture; null items become empty text. | Querying Sequences |
string JoinWith<T>(this IEnumerable<T> source, string separator) | Joins the items into one string with separator between them. Values are formatted with the invariant culture, so numbers and dates look the same on every machine; null items become empty text. | Querying Sequences |
(double Slope, double Intercept) LinearTrend(this IEnumerable<(double X, double Y)> points) | Fits a least-squares line y = Slope · x + Intercept through the points. | Statistics |
(double Slope, double Intercept) LinearTrend(this IEnumerable<double> source) | Fits a least-squares line through the values plotted against their index (0, 1, 2, ...): the slope is the average change per step and the intercept the fitted value at index 0. | Statistics |
bool MatchAll<TSource>(this IEnumerable<TSource> source, params TSource[] target) | Checks whether both sequences hold the same items the same number of times, in any order. | Querying Sequences |
bool MatchAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> target, IEqualityComparer<TSource>? comparer) | Checks whether both sequences hold the same items the same number of times, in any order. | Querying Sequences |
bool MatchAny<TSource>(this IEnumerable<TSource> source, params TSource[] target) | Checks whether the sequence contains at least one of the target items. | Querying Sequences |
bool MatchAny<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> target, IEqualityComparer<TSource>? comparer) | Checks whether the sequence contains at least one of the target items. | Querying Sequences |
decimal Median(this IEnumerable<decimal> source) | Gets the middle value; for an even count, the average of the two middle values. | Statistics |
double Median(this IEnumerable<double> source) | Gets the middle value; for an even count, the average of the two middle values. | Statistics |
double Median(this IEnumerable<int> source) | Gets the middle value; for an even count, the average of the two middle values. | Statistics |
double Median(this IEnumerable<long> source) | Gets the middle value; for an even count, the average of the two middle values. | Statistics |
(T Min, T Max) MinMax<T>(this IEnumerable<T> source) | Finds the smallest and the largest item in a single pass. | Querying Sequences |
(T Min, T Max) MinMax<T>(this IEnumerable<T> source, IComparer<T>? comparer) | Finds the smallest and the largest item under comparer in a single pass. | Querying Sequences |
T Mode<T>(this IEnumerable<T> source) | Gets the most frequent item. On a tie, the item that reached the winning count first wins. | Statistics |
bool None<T>(this IEnumerable<T> source) | Checks whether the sequence has no items — the readable opposite of Any(). | Querying Sequences |
bool None<T>(this IEnumerable<T> source, Func<T, bool> predicate) | Checks whether no item matches predicate — the readable opposite of Any(predicate). | Querying Sequences |
IOrderedEnumerable<string> OrderByNatural(this IEnumerable<string> source) | Sorts strings in natural order, ignoring case: "file2" before "file10", "v1.2.9" before "v1.2.10". For a case-sensitive sort use OrderBy(x => x, NaturalStringComparer.Ordinal). | Fuzzy and Natural Sort |
IOrderedEnumerable<T> OrderByNatural<T>(this IEnumerable<T> source, Func<T, string?> keySelector) | Sorts items by a string key in natural order, ignoring case. | Fuzzy and Natural Sort |
IOrderedEnumerable<string> OrderByNaturalDescending(this IEnumerable<string> source) | Sorts strings in descending natural order, ignoring case: "file10" before "file2". | Fuzzy and Natural Sort |
IOrderedEnumerable<T> OrderByNaturalDescending<T>(this IEnumerable<T> source, Func<T, string?> keySelector) | Sorts items by a string key in descending natural order, ignoring case. | Fuzzy and Natural Sort |
IEnumerable<(T Previous, T Current)> Pairwise<T>(this IEnumerable<T> source) | Returns each item paired with the one before it: [a, b, c] → (a, b), (b, c). | Querying Sequences |
IEnumerable<TResult> Pairwise<T, TResult>(this IEnumerable<T> source, Func<T, T, TResult> resultSelector) | Combines each item with the one before it, for example to compute differences between readings. | Querying Sequences |
(IReadOnlyList<T> Matched, IReadOnlyList<T> Unmatched) Partition<T>(this IEnumerable<T> source, Func<T, bool> predicate) | Splits the sequence in one pass into the items that match and the items that do not. | Querying Sequences |
decimal Percentile(this IEnumerable<decimal> source, double percentile) | Gets the value below which percentile percent of the values fall, interpolating between neighbours (the same method as Excel's PERCENTILE.INC). latencies.Percentile(95) is the p95 latency. | Statistics |
double Percentile(this IEnumerable<double> source, double percentile) | Gets the value below which percentile percent of the values fall, interpolating between neighbours (the same method as Excel's PERCENTILE.INC). latencies.Percentile(95) is the p95 latency. | Statistics |
double Percentile(this IEnumerable<int> source, double percentile) | Gets the value below which percentile percent of the values fall, interpolating between neighbours (the same method as Excel's PERCENTILE.INC). latencies.Percentile(95) is the p95 latency. | Statistics |
double Percentile(this IEnumerable<long> source, double percentile) | Gets the value below which percentile percent of the values fall, interpolating between neighbours (the same method as Excel's PERCENTILE.INC). latencies.Percentile(95) is the p95 latency. | Statistics |
IEnumerable<IReadOnlyList<T>> Permutations<T>(this IEnumerable<T> source) | Yields every ordering of the items, in lexicographic order of positions: the source order comes first and its reverse last. Items are permuted by position, so equal items produce repeated permutations. | Transforming Sequences |
IEnumerable<IReadOnlyList<T>> PowerSet<T>(this IEnumerable<T> source) | Yields every subset of the items, from the empty set to the full set, in increasing bitmask order (subset i contains item k when bit k of i is set). The items are read once, when this method is called, and there may be at most 30 of them (2^30 subsets). | Transforming Sequences |
IEnumerable<TResult> RecursiveJoin<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> parentKeySelector, Func<TSource, TKey> childKeySelector, Func<TSource, int, int, IEnumerable<TResult>, TResult> resultSelector) | Builds a tree from a flat list of items that point to their parent. | Transforming Sequences |
IEnumerable<TResult> RecursiveJoin<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> parentKeySelector, Func<TSource, TKey> childKeySelector, Func<TSource, int, IEnumerable<TResult>, TResult> resultSelector) | Builds a tree from a flat list of items that point to their parent. | Transforming Sequences |
IEnumerable<TResult> RecursiveJoin<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> parentKeySelector, Func<TSource, TKey> childKeySelector, Func<TSource, IEnumerable<TResult>, TResult> resultSelector) | Builds a tree from a flat list of items that point to their parent. | Transforming Sequences |
IEnumerable<TResult> RecursiveJoin<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> parentKeySelector, Func<TSource, TKey> childKeySelector, Func<TSource, int, int, IEnumerable<TResult>, TResult> resultSelector, IComparer<TKey>? comparer) | Builds a tree from a flat list of items that point to their parent. Runs in O(n log n), reads source once, and accepts null keys. | Transforming Sequences |
IEnumerable<TResult> RecursiveJoin<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> parentKeySelector, Func<TSource, TKey> childKeySelector, Func<TSource, int, IEnumerable<TResult>, TResult> resultSelector, IComparer<TKey>? comparer) | Builds a tree from a flat list of items that point to their parent. | Transforming Sequences |
IEnumerable<TResult> RecursiveJoin<TSource, TKey, TResult>(this IEnumerable<TSource> source, Func<TSource, TKey> parentKeySelector, Func<TSource, TKey> childKeySelector, Func<TSource, IEnumerable<TResult>, TResult> resultSelector, IComparer<TKey>? comparer) | Builds a tree from a flat list of items that point to their parent. | Transforming Sequences |
IEnumerable<TSource> RecursiveSelect<TSource>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>?> childSelector) | Flattens a tree depth-first: each item is followed by its descendants. | Transforming Sequences |
IEnumerable<TResult> RecursiveSelect<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>?> childSelector, Func<TSource, int, int, TResult> selector) | Flattens a tree depth-first and projects each item. | Transforming Sequences |
IEnumerable<TResult> RecursiveSelect<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>?> childSelector, Func<TSource, int, TResult> selector) | Flattens a tree depth-first and projects each item. | Transforming Sequences |
IEnumerable<TResult> RecursiveSelect<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>?> childSelector, Func<TSource, TResult> selector) | Flattens a tree depth-first and projects each item. | Transforming Sequences |
IEnumerable<T> Scan<T>(this IEnumerable<T> source, Func<T, T, T> accumulator) | Returns every intermediate result of an accumulation — running totals, running maximums, ... | Transforming Sequences |
IEnumerable<TAccumulate> Scan<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator) | Returns every intermediate result of an accumulation that starts from seed. | Transforming Sequences |
Task<TResult[]> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, Task<TResult>> selector, int maxConcurrency) | Runs an async operation for every item with at most maxConcurrency running at once, and returns the results in the original order. The usual way to call an API for 1,000 items without opening 1,000 connections. | Transforming Sequences |
Task<TResult[]> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, CancellationToken, Task<TResult>> selector, int maxConcurrency, CancellationToken cancellationToken) | Runs an async operation for every item with at most maxConcurrency running at once, and returns the results in the original order. After the first failure no new operations start, running ones receive a cancelled token, and the failure is rethrown. | Transforming Sequences |
IEnumerable<T> SkipUntil<T>(this IEnumerable<T> source, Func<T, bool> predicate) | Skips items up to but excluding the first one that matches predicate, then yields the rest, starting with that match. Unlike SkipWhile(x => !predicate(x)) the intent reads directly. | Querying Sequences |
IEnumerable<T> SortBy<T>(this IEnumerable<T> source, string? sort) | Sorts by a text specification such as "lastName desc, age" or "-createdAt", as received in an API's ?sort= parameter. Property names are case-insensitive and may be nested ("address.city"). Only public readable properties of a comparable type are accepted, so the text cannot run code. | Paging and Sorting |
IEnumerable<T> SortBy<T>(this IEnumerable<T> source, string? sort, params string[] allowedProperties) | Sorts by a text specification such as "lastName desc, age", accepting only the listed property names or nested paths. Use this overload for a sort that comes from a request. | Paging and Sorting |
IEnumerable<IEnumerable<TSource>> Split<TSource>(this TSource[] source, int size) | Splits an array into chunks of at most size items. The last chunk may be smaller. | Querying Sequences |
double StandardDeviation(this IEnumerable<decimal> source) | Gets the population standard deviation: how far values typically are from their average. (Divides by n. For a sample estimate, multiply by √(n / (n − 1)).) | Statistics |
double StandardDeviation(this IEnumerable<double> source) | Gets the population standard deviation: how far values typically are from their average. (Divides by n. For a sample estimate, multiply by √(n / (n − 1)).) | Statistics |
double StandardDeviation(this IEnumerable<int> source) | Gets the population standard deviation: how far values typically are from their average. (Divides by n. For a sample estimate, multiply by √(n / (n − 1)).) | Statistics |
double StandardDeviation(this IEnumerable<long> source) | Gets the population standard deviation: how far values typically are from their average. (Divides by n. For a sample estimate, multiply by √(n / (n − 1)).) | Statistics |
IEnumerable<T> TakeUntil<T>(this IEnumerable<T> source, Func<T, bool> predicate) | Yields items up to and including the first one that matches predicate, then stops. Unlike TakeWhile(x => !predicate(x)), the matching item itself is returned. | Querying Sequences |
string ToBarChart(this IEnumerable<(string Label, double Value)> source) | Draws a horizontal bar chart from (label, value) pairs: the label, a bar scaled to the largest value (40 characters wide, with eighth-cell precision) and the value. Negative values draw no bar. | Tables, Charts and Exports |
string ToBarChart(this IEnumerable<(string Label, double Value)> source, int width) | Draws a horizontal bar chart from (label, value) pairs: the label, a bar scaled to the largest value (width characters wide, with eighth-cell precision) and the value. Negative values draw no bar. | Tables, Charts and Exports |
string ToBarChart<T>(this IEnumerable<T> source, Func<T, string?> label, Func<T, double> value) | Draws a horizontal bar chart, one line per item: the label, a bar scaled to the largest value (40 characters wide, with eighth-cell precision) and the value. Negative values draw no bar. | Tables, Charts and Exports |
string ToBarChart<T>(this IEnumerable<T> source, Func<T, string?> label, Func<T, double> value, int width) | Draws a horizontal bar chart, one line per item: the label, a bar scaled to the largest value (width characters wide, with eighth-cell precision) and the value. Negative values draw no bar. | Tables, Charts and Exports |
string ToColumns(this IEnumerable<string?> source, int totalWidth) | Lays the strings out in columns like the ls command: filled top to bottom, then left to right, with as many columns as fit in totalWidth (two spaces between columns, each column as wide as its widest item). A string wider than totalWidth forces one item per line. | Tables, Charts and Exports |
string ToCsv<T>(this IEnumerable<T> source, char separator = ',', bool protectFormulas = true) | Writes the items as CSV (RFC 4180): a header row of property names, then one row per item. Fields with separators, quotes or line breaks are quoted; numbers use the invariant culture and dates ISO 8601. | Tables, Charts and Exports |
string ToHistogram(this IEnumerable<double> source, int buckets) | Draws a histogram: the values are split into buckets equal-width ranges between the smallest and the largest value (the largest value falls into the last range), one line per range with its bounds, a bar scaled to the fullest range (40 characters wide) and the count. Double.NaN values are skipped; a single distinct value gives one range holding everything. | Tables, Charts and Exports |
string ToHistogram(this IEnumerable<int> source, int buckets) | Draws a histogram: the values are split into buckets equal-width ranges between the smallest and the largest value (the largest value falls into the last range), one line per range with its bounds, a bar scaled to the fullest range (40 characters wide) and the count. Double.NaN values are skipped; a single distinct value gives one range holding everything. | Tables, Charts and Exports |
string ToHistogram(this IEnumerable<long> source, int buckets) | Draws a histogram: the values are split into buckets equal-width ranges between the smallest and the largest value (the largest value falls into the last range), one line per range with its bounds, a bar scaled to the fullest range (40 characters wide) and the count. Double.NaN values are skipped; a single distinct value gives one range holding everything. | Tables, Charts and Exports |
string ToHistogram(this IEnumerable<double> source, int buckets, int width) | Draws a histogram: the values are split into buckets equal-width ranges between the smallest and the largest value (the largest value falls into the last range), one line per range with its bounds, a bar scaled to the fullest range (width characters wide) and the count. Double.NaN values are skipped; a single distinct value gives one range holding everything. | Tables, Charts and Exports |
string ToHistogram(this IEnumerable<int> source, int buckets, int width) | Draws a histogram: the values are split into buckets equal-width ranges between the smallest and the largest value (the largest value falls into the last range), one line per range with its bounds, a bar scaled to the fullest range (width characters wide) and the count. Double.NaN values are skipped; a single distinct value gives one range holding everything. | Tables, Charts and Exports |
string ToHistogram(this IEnumerable<long> source, int buckets, int width) | Draws a histogram: the values are split into buckets equal-width ranges between the smallest and the largest value (the largest value falls into the last range), one line per range with its bounds, a bar scaled to the fullest range (width characters wide) and the count. Double.NaN values are skipped; a single distinct value gives one range holding everything. | Tables, Charts and Exports |
string ToHtmlTable<TSource>(this IEnumerable<TSource> source) | Renders the items as an HTML table: one column per public readable property, one row per item. Header names and cell values are HTML-encoded. | Tables, Charts and Exports |
string ToMarkdownTable<T>(this IEnumerable<T> source) | Renders the items as a Markdown table (GitHub style) with numeric columns right-aligned — paste it into a README, a pull request or a chat message. | Tables, Charts and Exports |
Page<T> ToPage<T>(this IEnumerable<T> source, int pageNumber, int pageSize) | Takes one page of items and counts the total, ready to return from an API. | Paging and Sorting |
IReadOnlyList<T> TopologicalSort<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>?> dependenciesSelector, IEqualityComparer<T>? comparer = null) | Orders items so that every item comes after the items it depends on. Dependencies that are not in source are included too. | Diff and Dependencies |
string ToSparkline(this IEnumerable<decimal> source) | Draws the values as a one-line sparkline of block characters (▁▂▃▄▅▆▇█), scaled between the smallest and the largest value. Equal values draw the middle block; Double.NaN draws a space. | Tables, Charts and Exports |
string ToSparkline(this IEnumerable<double> source) | Draws the values as a one-line sparkline of block characters (▁▂▃▄▅▆▇█), scaled between the smallest and the largest value. Equal values draw the middle block; Double.NaN draws a space. | Tables, Charts and Exports |
string ToSparkline(this IEnumerable<int> source) | Draws the values as a one-line sparkline of block characters (▁▂▃▄▅▆▇█), scaled between the smallest and the largest value. Equal values draw the middle block; Double.NaN draws a space. | Tables, Charts and Exports |
string ToSparkline(this IEnumerable<long> source) | Draws the values as a one-line sparkline of block characters (▁▂▃▄▅▆▇█), scaled between the smallest and the largest value. Equal values draw the middle block; Double.NaN draws a space. | Tables, Charts and Exports |
string ToTable<T>(this IEnumerable<T> source) | Renders the items as a box-drawn text table for consoles and logs: one column per public property, numbers right-aligned. A sequence of simple values (numbers, strings, ...) gets a single "Value" column. | Tables, Charts and Exports |
string ToTimeline<T>(this IEnumerable<T> source, Func<T, string?> label, Func<T, DateTime> start, Func<T, DateTime> end) | Draws a timeline (a text Gantt chart): one line per item with its label, a 40-character track where the item's interval is filled (█) and the rest dotted (·), and the start and end as offsets from the earliest start (0ms, 250ms, 1.2s, 3m 05s, 1h 02m). The track spans from the earliest start to the latest end; every interval fills at least one cell. | Tables, Charts and Exports |
string ToTimeline<T>(this IEnumerable<T> source, Func<T, string?> label, Func<T, TimeSpan> start, Func<T, TimeSpan> end) | Draws a timeline (a text Gantt chart) from offsets measured from a common zero, such as Stopwatch readings: one line per item with its label, a 40-character track where the item's interval is filled (█) and the rest dotted (·), and the start and end as offsets from the earliest start (0ms, 250ms, 1.2s, 3m 05s, 1h 02m). The track spans from the earliest start to the latest end; every interval fills at least one cell. | Tables, Charts and Exports |
string ToTimeline<T>(this IEnumerable<T> source, Func<T, string?> label, Func<T, DateTime> start, Func<T, DateTime> end, int width) | Draws a timeline (a text Gantt chart): one line per item with its label, a width-character track where the item's interval is filled (█) and the rest dotted (·), and the start and end as offsets from the earliest start (0ms, 250ms, 1.2s, 3m 05s, 1h 02m). The track spans from the earliest start to the latest end; every interval fills at least one cell. | Tables, Charts and Exports |
string ToTimeline<T>(this IEnumerable<T> source, Func<T, string?> label, Func<T, TimeSpan> start, Func<T, TimeSpan> end, int width) | Draws a timeline (a text Gantt chart) from offsets measured from a common zero, such as Stopwatch readings: one line per item with its label, a width-character track where the item's interval is filled (█) and the rest dotted (·), and the start and end as offsets from the earliest start (0ms, 250ms, 1.2s, 3m 05s, 1h 02m). The track spans from the earliest start to the latest end; every interval fills at least one cell. | Tables, Charts and Exports |
string ToTreeString<T>(this IEnumerable<T> roots, Func<T, IEnumerable<T>?> childrenSelector, Func<T, string?> labelSelector) | Renders several trees (a forest) one after another, each with branch lines. | Tables, Charts and Exports |
string ToTreeString<T>(this T root, Func<T, IEnumerable<T>?> childrenSelector, Func<T, string?> labelSelector) | Renders a tree with branch lines, like the tree command. | Tables, Charts and Exports |
IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> source, TSource item) | Produces the set union of the sequence and a single item. | Querying Sequences |
IEnumerable<T> WhereIf<T>(this IEnumerable<T> source, bool condition, Func<T, bool> predicate) | Filters the sequence only when condition is true. Removes the if (x) query = query.Where(...) boilerplate from optional filters. | Querying Sequences |
IEnumerable<IReadOnlyList<T>> Window<T>(this IEnumerable<T> source, int size) | Slides a window of size items over the sequence, one item at a time. | Transforming Sequences |
IEnumerable<(T Item, int Index)> WithIndex<T>(this IEnumerable<T> source) | Pairs every item with its zero-based position, for foreach loops that need the index. | Querying Sequences |
IEnumerable<double> WithoutOutliers(this IEnumerable<double> source) | Drops outliers using Tukey's fences: values below Q1 − 1.5·IQR or above Q3 + 1.5·IQR, with the quartiles computed like Percentile. The rest keep their order. Fewer than four values are returned unchanged. The sequence is buffered, not streamed. | Statistics |
IEnumerable<int> WithoutOutliers(this IEnumerable<int> source) | Drops outliers using Tukey's fences: values below Q1 − 1.5·IQR or above Q3 + 1.5·IQR, with the quartiles computed like Percentile. The rest keep their order. Fewer than four values are returned unchanged. The sequence is buffered, not streamed. | Statistics |
IEnumerable<long> WithoutOutliers(this IEnumerable<long> source) | Drops outliers using Tukey's fences: values below Q1 − 1.5·IQR or above Q3 + 1.5·IQR, with the quartiles computed like Percentile. The rest keep their order. Fewer than four values are returned unchanged. The sequence is buffered, not streamed. | Statistics |
IEnumerable<(T Item, EnumerationProgress Progress)> WithProgress<T>(this IEnumerable<T> source) | Pairs every item with where the loop is, for progress bars and status lines in command-line tools. The elapsed time is measured when each item is handed out, so the estimate reflects how long the loop body took for the previous items. Sequences whose count is not known up front (anything that is not an ICollection or IReadOnlyCollection) are buffered first. | Transforming Sequences |
EnumerationProgress
Where a loop is, as reported by WithProgress: the item's position, the elapsed time, and an estimate of the time remaining.
Namespace: System.Collections.Generic. Guide: Transforming Sequences.
| Member | Summary | Guide |
|---|---|---|
EnumerationProgress() | Creates an instance with every field set to its default value. | Transforming Sequences |
EnumerationProgress(int index, int total, TimeSpan elapsed) | Initializes a new instance of the EnumerationProgress struct. | Transforming Sequences |
int Completed { get; } | Gets the number of items handed out before the current one. | Transforming Sequences |
TimeSpan Elapsed { get; } | Gets the time since enumeration started, measured when the current item was handed out. | Transforming Sequences |
TimeSpan? EstimatedRemaining { get; } | Gets the estimated time until the loop finishes, assuming the remaining items take as long on average as the completed ones. null for the first item, when there is nothing to extrapolate from. | Transforming Sequences |
double Fraction { get; } | Gets the completed share, from 0 for the first item to just below 1 for the last. | Transforming Sequences |
int Index { get; } | Gets the zero-based position of the current item. | Transforming Sequences |
bool IsLast { get; } | Gets a value indicating whether the current item is the last one. | Transforming Sequences |
double Percent { get; } | Gets the completed share as a percentage, 0 to 100. | Transforming Sequences |
int Remaining { get; } | Gets the number of items still to come after the current one. | Transforming Sequences |
int Total { get; } | Gets the total number of items. | Transforming Sequences |
override bool Equals(object? obj) | Indicates whether this instance and another value are equal. | Transforming Sequences |
bool Equals(EnumerationProgress other) | Indicates whether this instance and another value are equal. | Transforming Sequences |
override int GetHashCode() | Returns the hash code for this instance. | Transforming Sequences |
override string ToString() | Describes the position for a status line: "3/10 (30%), 1.2s elapsed, ~2.8s left". | Transforming Sequences |
static bool operator !=(EnumerationProgress left, EnumerationProgress right) | Compares two snapshots. | Transforming Sequences |
static bool operator ==(EnumerationProgress left, EnumerationProgress right) | Compares two snapshots. | Transforming Sequences |
EnumExtensions
Extension methods for enums.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
TAttribute? GetAttribute<TAttribute>(this Enum value) | Gets an attribute applied to the enum member that value names. | Enums |
string GetDescription(this Enum value) | Gets the text of the member's DescriptionAttribute, or the member name if it has none. For a combination of flags, the descriptions are joined with ", ". | Enums |
IReadOnlyList<T> GetFlags<T>(this T value) | Splits a [Flags] value into the individual single-bit members it contains. | Enums |
bool HasAnyFlag<T>(this T value, T flags) | Checks whether at least one bit of flags is set in the value. Enum.HasFlag requires all of them. | Enums |
bool IsDefined<T>(this T value) | Checks whether the value is a declared member of its enum. For a [Flags] enum, a combination counts when every set bit is a declared single-bit member; zero counts only when a member is declared for it. | Enums |
T WithFlag<T>(this T value, T flag) | Returns the value with the bits of flag set. | Enums |
T WithoutFlag<T>(this T value, T flag) | Returns the value with the bits of flag cleared. | Enums |
ExceptionExtensions
Extension methods for Exception.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
TException? FindInnerException<TException>(this Exception exception) | Finds the first exception of type TException in the chain (the exception itself, its inner exceptions, and every branch of an AggregateException). | Exceptions and Disposables |
IReadOnlyList<Exception> GetAllExceptions(this Exception exception) | Returns the exception and every inner exception, outermost first, depth-first, including every branch of an AggregateException. | Exceptions and Disposables |
string GetAllMessages(this Exception exception, string separator = " --> ") | Joins the messages of the exception and all its inner exceptions, including every branch of an AggregateException. Good for one-line log entries. | Exceptions and Disposables |
Exception GetRootCause(this Exception exception) | Returns the innermost exception: the one that was thrown first and has no inner exception of its own. For an AggregateException the first inner exception is followed. | Exceptions and Disposables |
void Rethrow(this Exception exception) | Throws the exception again with its original stack trace intact, which a plain throw exception; would reset. Use it for an exception that was stored and is thrown later, outside its catch block. | Exceptions and Disposables |
FunctionExtensions
Extension methods for delegates and for chaining calls.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
Func<T, TResult> Compose<T, TIntermediate, TResult>(this Func<T, TIntermediate> first, Func<TIntermediate, TResult> second) | Chains two functions into one that applies first and then second: parse.Compose(validate) is input => validate(parse(input)). | Functions |
Action Debounce(this Action action, TimeSpan delay) | Wraps the action so it runs once, delay after the last call in a burst of calls. Typical for search-as-you-type, auto-save and file watchers. The action runs on a thread-pool thread and should handle its own exceptions. | Functions |
Func<CancellationToken, Task<T>> Latest<T>(this Func<CancellationToken, Task<T>> operation) | Wraps an async operation with "only the latest call matters" semantics: every call cancels the token handed to the previous call that is still running. The token given to the operation is linked with the caller's own token, so either cancels it. | Functions |
Func<TArg, CancellationToken, Task<T>> Latest<TArg, T>(this Func<TArg, CancellationToken, Task<T>> operation) | Wraps an async operation with "only the latest call matters" semantics, made for search-as-you-type: every call cancels the token handed to the previous call that is still running, so a stale response can never overtake a newer one. The token given to the operation is linked with the caller's own token, so either cancels it. | Functions |
Func<T, TResult> Memoize<T, TResult>(this Func<T, TResult> function) | Wraps the function so each argument is computed once and then served from a cache. Thread-safe: concurrent first calls for the same argument share one computation. A call that throws is not cached. Only memoize pure functions: same input, same output, no side effects. | Functions |
Func<T1, T2, TResult> Memoize<T1, T2, TResult>(this Func<T1, T2, TResult> function) | Wraps the two-argument function so each pair of arguments is computed once and then served from a cache. Thread-safe. Only memoize pure functions. | Functions |
Func<T, Task<TResult>> MemoizeAsync<T, TResult>(this Func<T, Task<TResult>> function) | Caches an async function per argument, forever. Concurrent callers for the same argument share one call, and a call that fails is not cached, so the next call retries. A call that never completes blocks its argument for as long as the returned delegate lives: every later caller for that argument waits on it. Put a timeout inside the function so a hung call fails and is retried, for example x => LoadAsync(x).WithTimeout(10.Seconds()). | Functions |
Func<T, Task<TResult>> MemoizeAsync<T, TResult>(this Func<T, Task<TResult>> function, TimeSpan timeToLive) | Caches an async function per argument for timeToLive — a small in-process cache for configuration, lookups or slow API calls. Concurrent callers for the same argument share one call, and a call that fails (or throws before returning its task) is not cached, so the next call retries. The time-to-live counts from when a call completes successfully: a call still in flight never expires, so callers keep sharing it even when it runs longer than timeToLive. A call that never completes therefore blocks its argument for as long as the returned delegate lives: every later caller for that argument waits on it, and neither the time-to-live nor the sweep removes it. Put a timeout inside the function so a hung call fails and is retried, for example x => LoadAsync(x).WithTimeout(10.Seconds()). Expired results are dropped when their argument is requested again, and also swept during calls (at most once per time-to-live, checked every 64 calls), so arguments that are never requested again do not keep their results in memory. No timer runs in the background. The cache has no size limit: every distinct argument requested within one time-to-live is held until it expires. | Functions |
Func<T, bool> Negate<T>(this Func<T, bool> predicate) | Returns the opposite predicate: items.Where(isValid.Negate()) keeps the invalid items. | Functions |
Action Once(this Action action) | Wraps the action so it runs only once; later calls do nothing. Thread-safe: calls that arrive while the first run is in progress wait for it. A run that throws does not count, so the next call tries again. | Functions |
Func<T> Once<T>(this Func<T> function) | Wraps the function so it runs only once and every later call returns the cached result. Thread-safe: concurrent first calls share one run. A run that throws is not cached, so the next call tries again. | Functions |
TResult Pipe<T, TResult>(this T value, Func<T, TResult> function) | Passes the value to a function, so calls read left to right instead of inside out: input.Pipe(Parse).Pipe(Validate).Pipe(Score) instead of Score(Validate(Parse(input))). | Functions |
Func<Task<T>> SingleFlight<T>(this Func<Task<T>> operation) | Wraps an async operation so that concurrent calls share one run: while a call is in flight, every other call returns the same task; once it completes (with a result or an exception) the next call starts a fresh run. Nothing is cached after completion. This is Go's singleflight, the fix for the "thundering herd" on a cache miss: a hundred requests arriving while the value is being loaded trigger one load, not a hundred. | Functions |
Func<TKey, Task<T>> SingleFlight<TKey, T>(this Func<TKey, Task<T>> operation) | Wraps a keyed async operation so that concurrent calls for the same key share one run: while a call for a key is in flight, every other call for that key returns the same task; different keys run independently. Once a run completes (with a result or an exception) the next call for its key starts a fresh run. Nothing is cached after completion. | Functions |
T Tap<T>(this T value, Action<T> action) | Runs a side effect on the value and returns the value unchanged — for logging inside a chain: order.Pipe(Price).Tap(p => logger.LogDebug("Priced {Total}", p.Total)).Pipe(Ship). | Functions |
Action Throttle(this Action action, TimeSpan interval) | Wraps the action so it runs at most once per interval. The first call runs right away; calls during the interval are merged into one more run at its end, so the latest state is never missed. Typical for scroll and resize handlers, progress reporting and live refreshes. The trailing run happens on a thread-pool thread; the action should handle its own exceptions. | Functions |
GroupingExtensions
Extension methods for IGrouping.
Namespace: System.Linq.
| Member | Summary | Guide |
|---|---|---|
void Deconstruct<TKey, TElement>(this IGrouping<TKey, TElement> grouping, out TKey key, out IEnumerable<TElement> items) | Deconstructs a group into its key and its items, so GroupBy results can be unpacked in a loop: foreach (var (status, orders) in orders.GroupBy(o => o.Status)) { ... }. | foreach and Deconstruction |
GuidExtensions
Extension methods for Guid.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
extension(Guid) bool IsEmpty { get; } | Indicates whether the value is Guid.Empty: if (order.Id.IsEmpty) { ... }. | C# 14 Extension Members |
extension(Guid) static string NewShortId() | Creates a new random id in the 22-character URL-safe form of ToShortId: string id = Guid.NewShortId();. | C# 14 Extension Members |
string ToShortId(this Guid guid) | Encodes the Guid as a 22-character URL-safe id (Base64url without padding) instead of 36 characters. Good for URLs, file names and anything a person might copy. Reverse it with FromShortId(). | Types and Reflection |
ListExtensions
Extension methods for IList.
Namespace: System.Collections.Generic.
| Member | Summary | Guide |
|---|---|---|
void AddFirst<T>(this IList<T> source, T item) | Inserts the item at the start of the list. | Lists and Collections |
void AddLast<T>(this IList<T> source, T item) | Adds the item at the end of the list. | Lists and Collections |
void Deconstruct<T>(this IList<T> source, out T first, out T second) | Deconstructs the first two items: var (name, domain) = email.Split('@');. Works on arrays and any IList. Extra items are ignored. | foreach and Deconstruction |
void Deconstruct<T>(this IList<T> source, out T first, out T second, out T third) | Deconstructs the first three items: var (year, month, day) = "2024-05-17".Split('-');. Extra items are ignored. | foreach and Deconstruction |
void Deconstruct<T>(this IList<T> source, out T first, out T second, out T third, out T fourth) | Deconstructs the first four items. Extra items are ignored. | foreach and Deconstruction |
void Deconstruct<T>(this IList<T> source, out T first, out T second, out T third, out T fourth, out T fifth) | Deconstructs the first five items. Extra items are ignored. | foreach and Deconstruction |
int FindIndex<T>(this IList<T> source, Predicate<T> selector) | Finds the index of the first item that matches selector. | Lists and Collections |
T GetOrAdd<T>(this IList<T> source, Func<T, bool> selector, Func<T> factory) | Returns the first item matching selector, or adds and returns a new one. Works for value types too (a match of 0 or default counts as found). | Lists and Collections |
void InsertAfter<T>(this IList<T> source, Predicate<T> selector, T item) | Inserts item right after the first item matching selector. If nothing matches, the item is inserted at the start. | Lists and Collections |
void InsertAfter<T>(this IList<T> source, T existingItem, T item) | Inserts item right after existingItem. If existingItem is not in the list, the item is inserted at the start. | Lists and Collections |
void InsertBefore<T>(this IList<T> source, Predicate<T> selector, T item) | Inserts item right before the first item matching selector. If nothing matches, the item is added at the end. | Lists and Collections |
void InsertBefore<T>(this IList<T> source, T existingItem, T item) | Inserts item right before existingItem. If existingItem is not in the list, the item is added at the end. | Lists and Collections |
void InsertRange<TSource>(this IList<TSource> source, int index, IEnumerable<TSource> items) | Inserts items into the list starting at index, keeping their order. | Lists and Collections |
bool MoveItem<T>(this IList<T> source, Predicate<T> selector, int targetIndex) | Moves the first item that matches selector to targetIndex. | Lists and Collections |
void ReplaceOne<T>(this IList<T> source, Predicate<T> selector, Func<T, T> itemFactory) | Replaces the first item that matches selector with the result of itemFactory. | Lists and Collections |
void ReplaceOne<T>(this IList<T> source, Predicate<T> selector, T item) | Replaces the first item that matches selector with item. | Lists and Collections |
void ReplaceOne<T>(this IList<T> source, T item, T replaceWith) | Replaces the first occurrence of item (by equality) with replaceWith. | Lists and Collections |
void ReplaceWhile<T>(this IList<T> source, Predicate<T> selector, Func<T, T> itemFactory) | Replaces every item that matches selector with the result of itemFactory. | Lists and Collections |
void ReplaceWhile<T>(this IList<T> source, Predicate<T> selector, T item) | Replaces every item that matches selector with item. | Lists and Collections |
MemberInfoExtensions
Attribute lookups on any MemberInfo: types, properties, methods, fields, events, constructors.
Namespace: System.Reflection.
| Member | Summary | Guide |
|---|---|---|
TAttribute? GetAttribute<TAttribute>(this MemberInfo member) | Gets the single TAttribute applied to the member, looking at inherited members too. | Types and Reflection |
TAttribute? GetAttribute<TAttribute>(this MemberInfo member, bool inherit) | Gets the single TAttribute applied to the member. | Types and Reflection |
IReadOnlyList<TAttribute> GetAttributes<TAttribute>(this MemberInfo member) | Gets every TAttribute applied to the member, including inherited ones. | Types and Reflection |
bool HasAttribute<TAttribute>(this MemberInfo member) | Checks whether the member has a TAttribute, inherited ones included. | Types and Reflection |
NameValueCollectionExtensions
Extension methods for NameValueCollection and key/value pairs.
Namespace: System.Collections.Specialized.
| Member | Summary | Guide |
|---|---|---|
string ToQueryString(this IEnumerable<KeyValuePair<string, string?>> collection) | Builds a URL query string from key/value pairs, for example a Dictionary<string, string>. Keys and values are percent-encoded (spaces become %20). | Dictionaries |
string ToQueryString(this NameValueCollection collection) | Builds a URL query string. A key with several values is repeated (a=1&a=2). Keys and values are percent-encoded (spaces become %20). | Dictionaries |
NaturalStringComparer
Compares strings the way people read them: runs of digits compare by numeric value, everything else ordinally, so "file2" sorts before "file10" and "v1.2.9" before "v1.2.10".
Namespace: System. Guide: Fuzzy and Natural Sort.
| Member | Summary | Guide |
|---|---|---|
static NaturalStringComparer Ordinal { get; } | Natural order, case-sensitive: "B2" sorts before "a10" (upper case first). | Fuzzy and Natural Sort |
static NaturalStringComparer OrdinalIgnoreCase { get; } | Natural order, ignoring case: "a10" sorts before "B2". | Fuzzy and Natural Sort |
int Compare(string? x, string? y) | Compares two strings in natural order. | Fuzzy and Natural Sort |
bool Equals(string? x, string? y) | Returns whether two strings are equal in natural order (NaturalStringComparer.Compare returns 0). | Fuzzy and Natural Sort |
int GetHashCode(string? obj) | Gets a hash code that agrees with NaturalStringComparer.Equals: it hashes the text with the leading zeros of every digit run stripped (and, when ignoring case, upper-cased). | Fuzzy and Natural Sort |
NumberExtensions
Small arithmetic helpers: parity, percentages, digits, ordinal words and linear rescaling.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
IReadOnlyList<int> Digits(this int value) | Gets the decimal digits of the number, most significant first, ignoring the sign: 1234 → [1, 2, 3, 4]. | Numbers |
IReadOnlyList<int> Digits(this long value) | Gets the decimal digits of the number, most significant first, ignoring the sign: 1234L → [1, 2, 3, 4]. | Numbers |
int DigitSum(this int value) | Adds up the decimal digits of the number, ignoring the sign: 1234 → 10, -99 → 18. | Numbers |
int DigitSum(this long value) | Adds up the decimal digits of the number, ignoring the sign: 1234L → 10. | Numbers |
bool IsEven(this int value) | Checks whether the number is even. Zero is even. | Numbers |
bool IsEven(this long value) | Checks whether the number is even. Zero is even. | Numbers |
bool IsOdd(this int value) | Checks whether the number is odd. Negative odd numbers count too. | Numbers |
bool IsOdd(this long value) | Checks whether the number is odd. Negative odd numbers count too. | Numbers |
decimal PercentOf(this decimal value, decimal total) | Gets which percentage of total the value is: 12.5m.PercentOf(50m) → 25. | Numbers |
double PercentOf(this double value, double total) | Gets which percentage of total the value is: 0.5.PercentOf(2.0) → 25. | Numbers |
double PercentOf(this int value, int total) | Gets which percentage of total the value is: 25.PercentOf(200) → 12.5. | Numbers |
double Rescale(this double value, double fromMin, double fromMax, double toMin, double toMax) | Maps the value linearly from one range to another: 0.5.Rescale(0, 1, 0, 100) → 50, celsius.Rescale(0, 100, 32, 212) → Fahrenheit. Values outside the source range are extrapolated; follow with Clamp to limit them. | Numbers |
decimal RoundTo(this decimal value, decimal step) | Rounds to the nearest multiple of step, halves away from zero: prices to 0.05. For whole quantities (packs of 6) use the RoundTo overload. The result is the exact nearest multiple of the step whenever that multiple fits in a decimal, including values near Decimal.MaxValue. A multiple that needs more than the 28-29 significant digits a decimal holds (a step with many digits, or a huge value with a fractional step) cannot be represented, so the closest decimal to it is returned. | Numbers |
double RoundTo(this double value, double step) | Rounds to the nearest multiple of step, halves away from zero. Prefer the decimal overload for money: binary floating point cannot represent 0.05 exactly. Huge values and tiny steps such as Double.Epsilon do not overflow: the result is infinite only when the nearest multiple is too large in magnitude for a double (for example double.MaxValue.RoundTo(1e308) is Double.PositiveInfinity). NaN and infinite values are returned unchanged. | Numbers |
int RoundTo(this int value, int step) | Rounds to the nearest multiple of step, halves away from zero: quantities to packs of 6. | Numbers |
long RoundTo(this long value, long step) | Rounds to the nearest multiple of step, halves away from zero. | Numbers |
string ToCompactString(this int number) | Shortens a large number for dashboards and badges: 950 → "950", 1500 → "1.5K", 1_250_000 → "1.3M", 3_000_000_000 → "3B". One decimal at most, rounded; uses the invariant culture. | Numbers |
string ToCompactString(this long number) | Shortens a large number: 1500 → "1.5K", 1_250_000 → "1.3M", 3_000_000_000 → "3B", 4e12 → "4T". Suffixes: K, M, B, T, Qa (10^15), Qi (10^18). | Numbers |
string ToFileSize(this int bytes) | Formats a byte count as a readable size with one decimal: bytes.Length.ToFileSize(). | Numbers |
string ToFileSize(this long bytes) | Formats a byte count as a readable size using 1024-based units, with one decimal: 0 B, 512 B, 1.5 KB, 3.2 GB. | Numbers |
string ToFileSize(this int bytes, int decimals) | Formats a byte count as a readable size. | Numbers |
string ToFileSize(this long bytes, int decimals) | Formats a byte count as a readable size using 1024-based units: 0 B, 512 B, 1.5 KB, 3.25 GB. | Numbers |
string ToFraction(this decimal value) | Writes the number as a mixed fraction with a denominator of at most 1000: 0.75 → "3/4", 1.75 → "1 3/4", 0.3333 → "1/3", 3.14159 → "3 16/113". The closest fraction wins; whole numbers have no fraction part. | Numbers |
string ToFraction(this double value) | Writes the number as a mixed fraction with a denominator of at most 1000: 0.75 → "3/4", 1.75 → "1 3/4", 0.3333 → "1/3", 3.14159 → "3 16/113". The closest fraction wins; whole numbers have no fraction part. | Numbers |
string ToFraction(this decimal value, int maxDenominator) | Writes the number as a mixed fraction whose denominator is at most maxDenominator, choosing the closest such fraction (Stern–Brocot search): 3.14159 → "3 16/113" with 1000, "3 1/7" with 10. | Numbers |
string ToFraction(this double value, int maxDenominator) | Writes the number as a mixed fraction whose denominator is at most maxDenominator, choosing the closest such fraction (Stern–Brocot search): 3.14159 → "3 16/113" with 1000, "3 1/7" with 10. | Numbers |
string ToOrdinal(this int number) | Formats the number as an English ordinal: 1st, 2nd, 3rd, 4th, 11th, 12th, 13th, 21st, 112th, ... | Numbers |
string ToOrdinalWords(this int number) | Writes the number as an English ordinal in words: 1 → "first", 12 → "twelfth", 21 → "twenty-first", 100 → "one hundredth", 101 → "one hundred first". Compare ToOrdinal ("21st") and ToWords ("twenty-one"). | Numbers |
string ToRoman(this int number) | Writes the number as a standard Roman numeral: 2024 → "MMXXIV". Reverse it with FromRoman(). | Numbers |
string ToWords(this int number) | Writes the number in English words: 123 → "one hundred twenty-three", -5 → "minus five". Useful for cheques, invoices and accessibility text. | Numbers |
string ToWords(this long number) | Writes the number in English words, up to the quintillions: 1_000_001 → "one million one". | Numbers |
ObjectExtensions
General-purpose extension methods for any object.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
void ApplyTo(this IEnumerable<PropertyChange> changes, object target) | Replays property changes onto another object: sets each change's PropertyChange.NewValue on target, following dotted paths (Address.City sets target.Address.City) and converting the value to the property's type when needed. Together with CompareProperties this is a patch: before.CompareProperties(after).ApplyTo(other) copies exactly what changed. A collection change sets the whole collection (the value itself, not a copy). | Compare, Clone and Map |
T As<T>(this object obj) | Casts the object to T. Reads well in fluent chains. | Objects |
IDictionary<string, object?> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public) | Copies the object's readable, non-indexer properties into a dictionary keyed by property name. | Objects |
IReadOnlyList<PropertyChange> CompareProperties<T>(this T before, T after) | Lists the public properties whose values differ between two objects. One line gives you an audit log entry. Nested objects are compared property by property and reported with a dotted path (Address.City); collections are compared item by item; other values use Object.Equals. | Compare, Clone and Map |
string ContentHash(this object? obj) | Fingerprints the object's content: the lower-case hex SHA-256 of its UTF-8 ToCanonicalJson. Two objects with the same public property names and values hash the same, whatever their runtime type, property order or dictionary implementation — a stable ETag, cache key or change detector for any object graph. It is content-based, not bound to the type: a Customer and an anonymous object with the same data share a hash. | JSON and Safe Logging |
T DeepClone<T>(this T source) | Creates a deep copy by serializing to JSON and back (System.Text.Json): nested objects and collections are copied too, so changing the copy never affects the original. Copies public properties (including ones with a private setter, and get-only collections) and public fields; works with records. Reference cycles throw JsonException. | Compare, Clone and Map |
T If<T>(this T obj, bool condition, Action<T> action) | Runs action only when condition is true, then returns the object. | Objects |
T If<T>(this T obj, bool condition, Func<T, T> func) | Applies func only when condition is true. Useful in fluent chains. | Objects |
bool Is<T>(this object? obj) | Checks whether the object is a T (or derives from / implements it). | Objects |
bool Is(this object? obj, Type type) | Checks whether the object is an instance of type (or derives from / implements it). Same rule as Is. | Objects |
bool IsIn<T>(this T item, params T[] list) | Checks whether the item is one of the given values. | Objects |
bool IsIn<T>(this T item, IEnumerable<T> items) | Checks whether the item is in the sequence. | Objects |
bool IsNumber(this object? value) | Checks whether the value is a boxed number: any integer or floating-point type, decimal, Half, Int128 or UInt128. | Objects |
TDestination MapTo<TDestination>(this object source) | Creates a TDestination and copies every public property with the same name (case-insensitive) — entity to DTO without a mapping library. Destination properties with a public setter, including an init setter (the object is brand new, as in an object initializer), are written; properties without a match, read-only properties and properties with a private, protected or internal setter are left as they are. Simple values are converted (int → string, string → enum, ...). Lists, arrays and sets are mapped item by item into new collections (List<OrderLine> → OrderLineDto[], HashSet<T>, Collection<T>, any concrete ICollection<T> with a parameterless constructor), a dictionary is copied into a new dictionary of the same type (its values are copied by reference), and nested classes are mapped into new instances. A nested struct (or nullable struct) of another type is created and filled; a struct of the same type is copied by value, so the reference-type members inside it (lists, objects) are shared with the source. Collection types without ICollection<T> (Queue<T>, Stack<T>, immutable collections) and classes that derive from a framework collection (List<T>, Collection<T>, HashSet<T>, ...) and hold state of their own beyond it (any instance field, private or readonly, or settable property their own types declare, such as class Team: List<string> with a Name) are assigned by reference when the property has the same type. A class that implements ICollection<T> itself (its items in a private field) is rebuilt item by item into a new instance, so the copy never shares the source's collection. | Compare, Clone and Map |
T To<T>(this object obj) | Converts the value to T using the invariant culture. Supports numbers, strings, enums (by name or number), Guid, TimeSpan, DateTimeOffset and other types with a TypeConverter. | Objects |
object To(this object obj, Type type) | Converts the value to type using the invariant culture. See To. | Objects |
string ToCanonicalJson(this object? obj) | Serializes the object to canonical JSON: property names sorted ordinally at every depth, no whitespace, invariant formatting, public properties only. Two objects with the same data produce the same text whatever the order their properties are declared in, so the result can be compared, hashed or signed. Arrays keep their order. | JSON and Safe Logging |
string ToJsonString(this object? obj, JsonSerializerOptions? options = null) | Serializes the object to JSON with System.Text.Json. | JSON and Safe Logging |
string ToQueryString(this object source, string? collectionSeparator = null) | Builds a URL query string from the object's public properties, in the format ASP.NET Core model binding reads: nested objects become Address.City=..., collections of values repeat the key (Ids=1&Ids=2) and collections of objects are indexed (Items[0].Name=...). Null properties are skipped. Keys are percent-encoded like values, so brackets appear as Items%5B0%5D.Name; ASP.NET Core decodes them. | Objects |
string ToSafeJson(this object? obj, params string[] additionalSensitiveNames) | Serializes the object to JSON with secrets masked, so it is safe to write to logs. Values of properties whose names look sensitive become "***", at any depth: names containing password, secret, token, apikey, privatekey, authorization, cookie, creditcard, cardnumber or connectionstring, and names that are exactly pin, cvv, cvc, ssn, pwd or otp (case-insensitive, ignoring '_' and '-'). | JSON and Safe Logging |
void TrimStringProperties(this object source) | Trims every public readable and writable string property of the object, in place. | Objects |
T UpdateFrom<T>(this T target, object source, bool ignoreNullValues = true) | Copies the matching public properties of source onto this object, with the same rules as MapTo, except that init setters are never written: this object already exists. By default null values in the source are skipped, which gives PATCH semantics for nullable source properties: only the fields the client sent are changed. In that mode a PATCH object is merged into the nested object the target already holds (deep PATCH), so its fields the source does not carry keep their values, while a complete value of the property type replaces an immutable value; collections are always replaced by a new mapped collection. | Compare, Clone and Map |
ObjectExtensions (System.Reflection)
Reflection helpers for reading and writing properties by name.
Namespace: System.Reflection.
| Member | Summary | Guide |
|---|---|---|
object? GetPropertyValue(this object target, string propertyName) | Reads a public instance property by name. | Objects |
T? GetPropertyValue<T>(this object target, string propertyName) | Reads a public instance property by name and casts it to T. | Objects |
void SetPropertyValue(this object target, object? value, string propertyName) | Writes a public instance property by name. | Objects |
Page<T>
One page of results plus the numbers a client needs to page through the rest. Created by ToPage(pageNumber, pageSize); serializes cleanly as an API response.
Namespace: System.Collections.Generic. Guide: Paging and Sorting.
| Member | Summary | Guide |
|---|---|---|
Page(IReadOnlyList<T> items, int pageNumber, int pageSize, int totalCount) | Initializes a new instance of the Page class. | Paging and Sorting |
bool HasNext { get; } | Gets a value indicating whether a page exists after this one. | Paging and Sorting |
bool HasPrevious { get; } | Gets a value indicating whether a page exists before this one. | Paging and Sorting |
IReadOnlyList<T> Items { get; } | Gets the items on this page. | Paging and Sorting |
int PageNumber { get; } | Gets the 1-based page number. | Paging and Sorting |
int PageSize { get; } | Gets the maximum number of items per page. | Paging and Sorting |
int TotalCount { get; } | Gets the number of items across all pages. | Paging and Sorting |
int TotalPages { get; } | Gets the number of pages; 0 when there are no items. | Paging and Sorting |
ProcessExtensions
Makes a Process awaitable: int exitCode = await Process.Start("git", "pull");.
Namespace: System.Diagnostics.
| Member | Summary | Guide |
|---|---|---|
TaskAwaiter<int> GetAwaiter(this Process? process) | Awaits the process and returns its exit code. | Await Anything |
Task<int> WaitForExitCodeAsync(this Process? process, CancellationToken cancellationToken) | Waits for the process to exit and returns its exit code. Cancelling stops the wait, not the process. On the netstandard2.1 build, asynchronously redirected output (OutputDataReceived) may still be arriving when this completes; the .NET 10 build waits for it. | Await Anything |
PropertyChange
One property that differs between two objects. Returned by CompareProperties.
Namespace: System. Guide: Compare, Clone and Map.
| Member | Summary | Guide |
|---|---|---|
PropertyChange(string propertyName, object? oldValue, object? newValue) | Initializes a new instance of the PropertyChange class. | Compare, Clone and Map |
object? NewValue { get; } | Gets the value after the change. | Compare, Clone and Map |
object? OldValue { get; } | Gets the value before the change. | Compare, Clone and Map |
string PropertyName { get; } | Gets the property name. | Compare, Clone and Map |
override string ToString() | Describes the change for logs: Email: "a@x.com" -> "b@x.com". | Compare, Clone and Map |
QueryableExtensions
Extension methods for IQueryable. These keep the filter as an expression, so ORMs such as Entity Framework still translate it to SQL.
Namespace: System.Linq.
| Member | Summary | Guide |
|---|---|---|
IQueryable<T> SortBy<T>(this IQueryable<T> source, string? sort) | Sorts the query by a text specification such as "lastName desc, age" or "-createdAt", as received in an API's ?sort= parameter. The sort is built as an expression, so Entity Framework runs it in SQL. Only public readable properties of a comparable type are accepted, case-insensitive and nested with dots, so the text cannot inject code. | Paging and Sorting |
IQueryable<T> SortBy<T>(this IQueryable<T> source, string? sort, params string[] allowedProperties) | Sorts the query by a text specification such as "lastName desc, age", accepting only the listed property names or nested paths. Use this overload for a sort that comes from a request. | Paging and Sorting |
Page<T> ToPage<T>(this IQueryable<T> source, int pageNumber, int pageSize) | Takes one page and counts the total, as two database queries (COUNT, then OFFSET/FETCH) with Entity Framework. When the requested page starts at or past the total, only the COUNT query runs. | Paging and Sorting |
IQueryable<T> WhereIf<T>(this IQueryable<T> source, bool condition, Expression<Func<T, bool>> predicate) | Filters the query only when condition is true. | Querying Sequences |
RandomExtensions
Extension methods for Random: picking, shuffling and generating values of common types. Every method draws from the Random it is given, so a seeded instance gives repeatable results. None of them is cryptographically secure.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
bool NextBool(this Random random) | Flips a fair coin. | Random |
bool NextBool(this Random random, double probabilityOfTrue) | Returns true with the given probability. | Random |
DateTime NextDateTime(this Random random, DateTime minInclusive, DateTime maxExclusive) | Returns a point in time in [minInclusive, maxExclusive) with tick precision. The result has the DateTime.Kind of minInclusive. | Random |
decimal NextDecimal(this Random random, decimal minInclusive, decimal maxExclusive) | Returns a decimal in [minInclusive, maxExclusive), with the resolution of a double (about 15 significant digits). | Random |
double NextDouble(this Random random, double minInclusive, double maxExclusive) | Returns a double in [minInclusive, maxExclusive). | Random |
T NextEnum<T>(this Random random) | Picks one of the declared members of an enum. | Random |
double NextGaussian(this Random random) | Returns a normally distributed value with mean 0 and standard deviation 1 (Box–Muller transform). | Random |
double NextGaussian(this Random random, double mean, double standardDeviation) | Returns a normally distributed value with the given mean and standard deviation. | Random |
Guid NextGuid(this Random random) | Returns a Guid built from 16 random bytes, with the version and variant bits of a version 4 GUID. It is repeatable for a seeded Random and not cryptographically random, so use it for test data, not for identifiers that must be unguessable. | Random |
T NextItem<T>(this Random random, IReadOnlyList<T> items) | Picks one item at random. | Random |
T NextItem<T>(this Random random, T first, T second, params T[] others) | Picks one of two or more values at random. | Random |
IReadOnlyList<T> NextItems<T>(this Random random, IReadOnlyList<T> items, int count) | Picks count distinct items (no item is picked twice), in random order. | Random |
string NextString(this Random random, int length) | Returns a string of random ASCII letters and digits (A–Z, a–z, 0–9). | Random |
string NextString(this Random random, int length, string alphabet) | Returns a string of random characters drawn from alphabet. | Random |
TimeSpan NextTimeSpan(this Random random, TimeSpan minInclusive, TimeSpan maxExclusive) | Returns a duration in [minInclusive, maxExclusive) with tick precision. | Random |
void Shuffle<T>(this Random random, IList<T> list) | Shuffles the list in place (Fisher–Yates), so every ordering is equally likely. | Random |
RangeExtensions
Makes C# ranges enumerable and queryable. Like slicing, the end is exclusive. Both ends must count from the start (no ^, no open end).
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
IEnumerable<int> AsEnumerable(this Range range) | Returns the range as a sequence, so any LINQ method can follow: (1..6).AsEnumerable().Sum(). | foreach and Deconstruction |
RangeExtensions.RangeEnumerator GetEnumerator(this Range range) | Enumerates the integers in the range. Like slicing, the end is exclusive: 0..3 yields 0, 1, 2. A range whose start is not below its end yields nothing. | foreach and Deconstruction |
IEnumerable<TResult> Select<TResult>(this Range range, Func<int, TResult> selector) | Projects each number. Enables from i in 0..10 select i * i. | foreach and Deconstruction |
IEnumerable<TResult> SelectMany<TCollection, TResult>(this Range range, Func<int, IEnumerable<TCollection>> collectionSelector, Func<int, TCollection, TResult> resultSelector) | Combines each number with an inner sequence. Enables from i in 0..3 from name in names select .... | foreach and Deconstruction |
IEnumerable<TResult> SelectMany<TResult>(this Range range, Func<int, Range> innerRangeSelector, Func<int, int, TResult> resultSelector) | Combines each number with an inner range. Enables from x in 0..3 from y in 0..x select (x, y). | foreach and Deconstruction |
IEnumerable<int> Step(this Range range, int step) | Enumerates the range with a step. A negative step counts down from the start towards the end: (10..0).Step(-3) yields 10, 7, 4, 1. | foreach and Deconstruction |
IEnumerable<int> Where(this Range range, Func<int, bool> predicate) | Filters the numbers. Enables from i in 0..10 where i % 2 == 0 select i. | foreach and Deconstruction |
RangeExtensions.RangeEnumerator
Enumerates the integers of a Range. Returned by GetEnumerator.
Namespace: System. Guide: foreach and Deconstruction.
| Member | Summary | Guide |
|---|---|---|
RangeEnumerator() | Creates an instance with every field set to its default value. | foreach and Deconstruction |
int Current { get; } | Gets the current number. | foreach and Deconstruction |
bool MoveNext() | Moves to the next number. | foreach and Deconstruction |
Retry
Retries a failing async operation with exponential backoff. Waits initialDelay after the first failure, then twice as long after each next one (200 ms, 400 ms, 800 ms, ...). The last failure is rethrown unchanged.
Namespace: System.Threading.Tasks. Guide: Tasks and Resilience.
| Member | Summary | Guide |
|---|---|---|
static Task ExecuteAsync(Func<Task> operation, int maxAttempts, TimeSpan initialDelay) | Runs operation until it succeeds or maxAttempts attempts have failed. | Tasks and Resilience |
static Task<T> ExecuteAsync<T>(Func<Task<T>> operation, int maxAttempts, TimeSpan initialDelay) | Runs operation until it succeeds or maxAttempts attempts have failed. | Tasks and Resilience |
static Task ExecuteAsync(Func<CancellationToken, Task> operation, int maxAttempts, TimeSpan initialDelay, Func<Exception, bool>? shouldRetry, CancellationToken cancellationToken) | Runs operation until it succeeds, a failure is not worth retrying, or maxAttempts attempts have failed. | Tasks and Resilience |
static Task<T> ExecuteAsync<T>(Func<CancellationToken, Task<T>> operation, int maxAttempts, TimeSpan initialDelay, Func<Exception, bool>? shouldRetry, CancellationToken cancellationToken) | Runs operation until it succeeds, a failure is not worth retrying, or maxAttempts attempts have failed. | Tasks and Resilience |
SemaphoreSlimExtensions
Turns SemaphoreSlim into an async-friendly lock you can release with using.
Namespace: System.Threading.
| Member | Summary | Guide |
|---|---|---|
IDisposable Lock(this SemaphoreSlim semaphore, CancellationToken cancellationToken = default) | Blocks until the semaphore is free and returns a handle that releases it when disposed. Prefer LockAsync in async code. | Await Anything |
Task<IDisposable> LockAsync(this SemaphoreSlim semaphore, CancellationToken cancellationToken = default) | Waits for the semaphore and returns a handle that releases it when disposed. | Await Anything |
StreamExtensions
Extension methods for Stream. The stream is read from its current position and is not disposed.
Namespace: System.IO.
| Member | Summary | Guide |
|---|---|---|
Task<string> ReadAllTextAsync(this Stream stream, Encoding? encoding = null, CancellationToken cancellationToken = default) | Reads the rest of the stream as text — a request body, a downloaded file, an embedded resource. A byte-order mark, if present, picks the encoding; otherwise encoding (UTF-8 by default). | IO and System |
Task<byte[]> ToByteArrayAsync(this Stream stream, CancellationToken cancellationToken = default) | Reads the rest of the stream into a byte array. | IO and System |
StringComparerExtensions
Extension members for StringComparer.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
extension(StringComparer) static NaturalStringComparer Natural { get; } | Natural order, case-sensitive: "file2" sorts before "file10". | C# 14 Extension Members |
extension(StringComparer) static NaturalStringComparer NaturalIgnoreCase { get; } | Natural order, ignoring case. | C# 14 Extension Members |
StringExtensions
Extension methods for string.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
extension(string?) bool IsBlank { get; } | Indicates whether the text is null, empty or only white space: if (name.IsBlank) { ... }. | C# 14 Extension Members |
string? Between(this string str, string start, string end, StringComparison comparisonType = StringComparison.Ordinal) | Gets the text between the first start and the next end after it. | Manipulation and Layout |
string Center(this string text, int width) | Centers the text in a field of width spaces. When the padding is odd, the extra space goes on the right. Text that is already as wide as the field is returned unchanged. | Manipulation and Layout |
string Center(this string text, int width, char padding) | Centers the text in a field of width characters. When the padding is odd, the extra character goes on the right. Text that is already as wide as the field is returned unchanged. | Manipulation and Layout |
string CommonPrefix(this string text, string other) | Returns the longest prefix (ordinal) that both strings share. | Predicates and Matching |
string CommonSuffix(this string text, string other) | Returns the longest suffix (ordinal) that both strings share. | Predicates and Matching |
string Compress(this string text) | Compresses the text (UTF-8, GZip) and returns it as Base64 — a compact form for storing or sending large text such as JSON. Reverse it with Decompress. | Text and Humanizing |
bool Contains(this string? str, params string[] toCheck) | Checks whether the string contains any of the given values (ordinal, case-sensitive). | Predicates and Matching |
bool ContainsAny(this string? str, StringComparison comparison, params string[] toCheck) | Checks whether the string contains any of the given values using the given comparison. | Predicates and Matching |
bool ContainsIgnoreCase(this string? str, params string[] toCheck) | Checks whether the string contains any of the given values, ignoring case (ordinal). | Predicates and Matching |
bool ContainsIgnoreCase(this string? str, string toCheck) | Checks whether the string contains toCheck, ignoring case (ordinal). | Predicates and Matching |
bool ContainsIgnoreCase(this string? str, StringComparison comp, params string[] toCheck) | Checks whether the string contains any of the given values using the given comparison. | Predicates and Matching |
bool ContainsIgnoreCase(this string? str, StringComparison comp, string toCheck) | Checks whether the string contains toCheck using the given comparison. | Predicates and Matching |
int CountOccurrences(this string? text, char value) | Counts how many times the character occurs. | Predicates and Matching |
int CountOccurrences(this string? text, string value) | Counts the non-overlapping occurrences of value (ordinal, case-sensitive). | Predicates and Matching |
int CountOccurrences(this string? text, string value, StringComparison comparison) | Counts the non-overlapping occurrences of value. | Predicates and Matching |
string Decompress(this string compressed, long maxDecompressedBytes = 67108864) | Restores text produced by Compress. Stops at maxDecompressedBytes (64 MB by default) so a small hostile input cannot expand into gigabytes (a "zip bomb"). | Text and Humanizing |
string Dedent(this string text) | Removes the leading whitespace that every non-blank line has in common (like Python's textwrap.dedent). Whitespace-only lines become empty. Handy with raw string literals, whose indentation follows the code rather than the output. | Manipulation and Layout |
string Dedent(this string text, int count) | Removes up to count leading spaces or tabs from every line (a tab counts as one). | Manipulation and Layout |
string DiffLines(this string oldText, string newText) | Compares two texts line by line and shows the differences like git diff: removed lines start with "- ", added lines with "+ ", unchanged lines with two spaces. Line endings are normalized first, and an empty text has no lines (so diffing against "" shows every line as added or removed). Line content is kept exactly, trailing white space included, so a change in trailing white space shows up; an empty line is written as just its marker ("-" or "+"), or as an empty line when unchanged. | Text and Humanizing |
bool EndsWith(this string? str, params string[] toCheck) | Checks whether the string ends with any of the given values (ordinal, case-sensitive). | Predicates and Matching |
bool EndsWithIgnoreCase(this string? str, params string[] toCheck) | Checks whether the string ends with any of the given values, ignoring case (ordinal). | Predicates and Matching |
bool EndsWithIgnoreCase(this string? str, string toCheck) | Checks whether the string ends with toCheck, ignoring case (ordinal). | Predicates and Matching |
string EnsureEndsWith(this string str, char c, StringComparison comparisonType = StringComparison.Ordinal) | Adds c to the end of the string if it does not already end with it. | Manipulation and Layout |
string EnsureStartsWith(this string str, char c, StringComparison comparisonType = StringComparison.Ordinal) | Adds c to the start of the string if it does not already start with it. | Manipulation and Layout |
bool EqualsIgnoreCase(this string? str, string? toCheck) | Compares two strings ignoring case (ordinal). Two null strings are equal. | Predicates and Matching |
IReadOnlyList<string> ExtractEmails(this string text) | Finds every e-mail address in free text, in order of appearance. | Parsing |
IReadOnlyList<string> ExtractUrls(this string text) | Finds every http and https URL in free text, in order of appearance. Trailing sentence punctuation ("see https://example.com.") is not part of the URL. | Parsing |
IReadOnlyDictionary<string, string>? ExtractWith(this string text, string template) | Reads values out of text using a template — the reverse of FormatWith. The template is literal text and {name} placeholders. Literal text must match exactly (ordinal, so case-sensitive); when the template starts or ends with literal text, the text must start or end with it. In between, each placeholder captures at least one character and ends at the first occurrence of the literal text that follows it; the last placeholder takes everything up to the template's closing literal text (or to the end of the text), line breaks included. Matching is a single forward scan with no backtracking, so hostile text cannot make it slow. | Parsing |
string FirstCharToUpper(this string input) | Upper-cases the first character (invariant culture) and leaves the rest unchanged. | Casing and Slugs |
string FormatWith(this string template, object source, IFormatProvider? provider = null) | Fills named placeholders from an object's properties (or a dictionary's keys), like string interpolation for templates stored as data — e-mails, notifications, messages from a database. Supports formats ({Total:C}, {Date:yyyy-MM-dd}), nested paths ({Address.City}) and {{ / }} for literal braces. | Text and Humanizing |
string FromBase64(this string base64) | Decodes Base64 text produced by ToBase64 back to a UTF-8 string. | Encoding and Validation |
string FromBase64(this string base64, Encoding encoding) | Decodes Base64 text back to a string using the given encoding. | Encoding and Validation |
byte[] FromBase64Url(this string base64Url) | Decodes Base64url text (with or without padding) into bytes. The reverse of ToBase64Url(). Only the Base64url alphabet is accepted (A-Z, a-z, 0-9, '-', '_'), optionally followed by up to two '=' padding characters; standard Base64 ('+', '/') and white space are rejected. | Encoding and Validation |
long FromCompactString(this string text) | Reads a shortened number back: "1.3M" → 1_300_000, "2k" → 2_000, "3.5B" → 3_500_000_000, "1,250" → 1250, "12 K" → 12_000. The reverse of ToCompactString(); suffixes K, M, B, T, Qa (10^15) and Qi (10^18), case-insensitive, with optional spaces and sign. The result is rounded to the nearest whole number. | Parsing |
byte[] FromHex(this string hex) | Decodes hex text (any case, no separators) into bytes. The reverse of ToHex(). | Encoding and Validation |
T? FromJson<T>(this string json, JsonSerializerOptions? options = null) | Deserializes a JSON string with System.Text.Json. The counterpart of ToJsonString. | JSON and Safe Logging |
int FromRoman(this string roman) | Parses a Roman numeral (case-insensitive) such as "MMXXIV". Only standard forms are accepted. | Text and Humanizing |
Guid FromShortId(this string shortId) | Turns a 22-character short id (from ToShortId) back into the Guid. | Text and Humanizing |
byte[] GetBytes(this string str) | Converts the string to bytes using UTF-8. | Encoding and Validation |
byte[] GetBytes(this string str, Encoding encoding) | Converts the string to bytes using the given encoding. | Encoding and Validation |
string Highlight(this string text, string term, string before, string after, bool htmlEncode = true) | Wraps every occurrence of term (case-insensitive) in before and after — for search results. By default the text and term are HTML-encoded first, so the result is safe to render as HTML; only your markers are markup. | Text and Humanizing |
string Indent(this string text, int count) | Puts count spaces in front of every line, including the first. Blank lines stay empty, and the line-ending style of the text (\r\n or \n) is kept. | Manipulation and Layout |
string Indent(this string text, string prefix) | Puts prefix in front of every line, including the first. Blank lines stay empty, and the line-ending style of the text (\r\n or \n) is kept. | Manipulation and Layout |
bool IsAnagramOf(this string? text, string other) | Checks whether the two strings are anagrams: the same letters and digits in any order, ignoring case, whitespace and punctuation. Two strings without any letter or digit count as anagrams. | Predicates and Matching |
bool IsBase64(this string? base64String) | Checks whether the string is valid, unpadded-by-whitespace Base64. | Predicates and Matching |
bool IsNullOrEmpty(this string? str) | Indicates whether this string is null or String.Empty. | Predicates and Matching |
bool IsNullOrWhiteSpace(this string? str) | Indicates whether this string is null, empty, or only white-space characters. | Predicates and Matching |
bool IsValidCreditCard(this string? number) | Checks whether the text is a plausible payment card number: 12 to 19 digits (spaces and dashes allowed) that pass the Luhn checksum. It catches typos; it does not prove the card exists. | Text and Humanizing |
bool IsValidEmail(this string? email) | Checks whether the text looks like a deliverable e-mail address (a practical subset of RFC 5322: name@domain.tld, ASCII only, no spaces, no display names, at most 64 characters before the '@' and 254 in all). Leading or trailing white space, including a final line break, makes it invalid. | Encoding and Validation |
bool IsValidIban(this string? iban) | Checks an IBAN (international bank account number) with the ISO 13616 mod-97 checksum. Spaces are allowed and case is ignored. Every other character must be an ASCII letter (A-Z, a-z) or an ASCII digit (0-9); other Unicode letters and digits (Arabic-Indic, full-width, ...) make it invalid. Country-specific lengths are not checked. | Encoding and Validation |
bool IsValidJson(this string? json) | Checks whether the text is valid JSON (any value: object, array, string, number, true, false, null). | JSON and Safe Logging |
bool IsValidUrl(this string? url) | Checks whether the text is an absolute http or https URL. The text is not trimmed: any white space, control character (such as a line break) or invisible format character (such as U+200B zero-width space, U+2060 word joiner, U+FEFF, a bidirectional control such as U+202E, or a character outside the BMP such as the language tag U+E0001 or a stray tag character U+E0041) anywhere in the text returns false, and so does an unpaired surrogate, even where Uri would percent-encode it, so the raw text is safe to reuse as validated. Some format characters that legitimate text needs are allowed in context. U+200C zero-width non-joiner and U+200D zero-width joiner are allowed only between two non-ASCII characters that are not joiners (emoji ZWJ sequences such as a family emoji, Persian and Indic words), and never in the user information before the '@'. Tag characters are allowed only in an emoji subdivision flag (such as the England, Scotland and Wales flags): U+1F3F4 waving black flag, then one to seven tag digits U+E0030–U+E0039 or lower-case tag letters U+E0061–U+E007A, then the cancel tag U+E007F; any other tag character (such as hidden ASCII text after a symbol) returns false. In the host, tag characters are never allowed, and a joiner must also meet the IDNA2008 context rules: it follows a virama (such as U+094D in Devanagari), or it is a U+200C between two joining letters (Arabic, Persian, Syriac, ...); where the platform's IDN support is available (not in globalization-invariant mode), the host must also convert to its ASCII (punycode) form. | Encoding and Validation |
string Left(this string str, int len) | Gets the first len characters of the string. | Manipulation and Layout |
int LevenshteinDistance(this string str, string other, bool ignoreCase = false) | Counts the single-character edits (insert, delete, replace) needed to turn one string into the other. | Fuzzy and Natural Sort |
string Mask(this string str, int keepStart = 0, int keepEnd = 4, char maskChar = '*') | Hides the middle of a sensitive value, such as a card number or token. If nothing would be hidden (the string is too short), every character is masked. | Manipulation and Layout |
string MaskEmail(this string email, char maskChar = '*') | Hides the local part of an e-mail address except its first character. The domain stays visible. A value without '@' is masked completely. | Manipulation and Layout |
bool MatchesWildcard(this string? text, string pattern) | Checks whether the text matches a glob pattern, where * stands for any run of characters (even none) and ? for exactly one. Ordinal and case-sensitive. The match is iterative, so hostile patterns cannot cause deep recursion or catastrophic backtracking. | Predicates and Matching |
bool MatchesWildcard(this string? text, string pattern, bool ignoreCase) | Checks whether the text matches a glob pattern, where * stands for any run of characters (even none) and ? for exactly one. Ordinal. The match is iterative, so hostile patterns cannot cause deep recursion or catastrophic backtracking. | Predicates and Matching |
string MinifyJson(this string json) | Removes all insignificant white space from JSON, for storage or transport. | JSON and Safe Logging |
string NormalizeLineEndings(this string str) | Converts every line ending (\r\n, \r, \n) to Environment.NewLine. | Manipulation and Layout |
string NormalizeWhitespace(this string text) | Collapses every run of whitespace (spaces, tabs, line breaks) into a single space and trims the ends. | Manipulation and Layout |
int NthIndexOf(this string str, char c, int n) | Gets the index of the nth occurrence of c. | Manipulation and Layout |
IReadOnlyDictionary<string, string> ParseKeyValuePairs(this string text) | Parses connection-string style text ("Server=x;Database=y") into a dictionary with case-insensitive keys. Keys and values are trimmed, empty segments are skipped, a segment without = becomes a key with an empty value, and when a key repeats the last value wins. | Parsing |
IReadOnlyDictionary<string, string> ParseKeyValuePairs(this string text, char pairSeparator, char keyValueSeparator) | Parses key<sep>value pairs separated by pairSeparator into a dictionary with case-insensitive keys. Keys and values are trimmed, empty segments are skipped, a segment without keyValueSeparator becomes a key with an empty value, and when a key repeats the last value wins. Only the first keyValueSeparator of a segment splits it, so values may contain it. | Parsing |
string Pluralize(this string word) | Returns the English plural of a singular word or phrase: "box" → "boxes", "city" → "cities", "person" → "people", "credit card" → "credit cards". Keeps the word's capitalization. | Text and Humanizing |
string PrettyJson(this string json) | Re-formats JSON with indentation for reading. | JSON and Safe Logging |
TimeSpan ReadingTime(this string text) | Estimates how long the text takes to read at 200 words per minute. | Text and Humanizing |
TimeSpan ReadingTime(this string text, int wordsPerMinute) | Estimates how long the text takes to read. | Text and Humanizing |
string RemoveDiacritics(this string str) | Removes accents and other combining marks: "Crème brûlée" → "Creme brulee". Also removes Arabic diacritics (harakat). | Casing and Slugs |
string? RemovePostFix(this string? str, params string[] postFixes) | Removes the first matching postfix from the end of the string (ordinal). | Manipulation and Layout |
string? RemovePostFix(this string? str, StringComparison comparisonType, params string[] postFixes) | Removes the first matching postfix from the end of the string. | Manipulation and Layout |
string? RemovePreFix(this string? str, params string[] preFixes) | Removes the first matching prefix from the start of the string (ordinal). | Manipulation and Layout |
string? RemovePreFix(this string? str, StringComparison comparisonType, params string[] preFixes) | Removes the first matching prefix from the start of the string. | Manipulation and Layout |
string Repeat(this char character, int count) | Repeats the character count times. | Manipulation and Layout |
string Repeat(this string text, int count) | Repeats the text count times. | Manipulation and Layout |
string ReplaceFirst(this string str, string search, string? replace, StringComparison comparisonType = StringComparison.Ordinal) | Replaces only the first occurrence of search. | Manipulation and Layout |
string ReverseText(this string text) | Reverses the text by user-visible characters, so emoji, flags and accented letters stay intact: "👍🏽 héllo" becomes "olléh 👍🏽" (a plain char reverse would break them). | Text and Humanizing |
string Right(this string str, int len) | Gets the last len characters of the string. | Manipulation and Layout |
double Similarity(this string str, string other, bool ignoreCase = false) | Scores how alike two strings are, from 0.0 (nothing in common) to 1.0 (identical). Based on LevenshteinDistance; handy for "did you mean ...?" suggestions. | Fuzzy and Natural Sort |
string[] Split(this string str, string separator) | Splits the string by a string separator. | Manipulation and Layout |
string[] Split(this string str, string separator, StringSplitOptions options) | Splits the string by a string separator. | Manipulation and Layout |
string[] SplitToLines(this string str) | Splits the string into lines. Recognizes \r\n, \r and \n on every platform. | Manipulation and Layout |
string[] SplitToLines(this string str, StringSplitOptions options) | Splits the string into lines. Recognizes \r\n, \r and \n on every platform. | Manipulation and Layout |
string[] SplitWords(this string str) | Splits PascalCase, camelCase, snake_case, kebab-case or plain text into its words. | Casing and Slugs |
bool StartsWith(this string? str, params string[] toCheck) | Checks whether the string starts with any of the given values (ordinal, case-sensitive). | Predicates and Matching |
bool StartsWithIgnoreCase(this string? str, params string[] toCheck) | Checks whether the string starts with any of the given values, ignoring case (ordinal). | Predicates and Matching |
bool StartsWithIgnoreCase(this string? str, string toCheck) | Checks whether the string starts with toCheck, ignoring case (ordinal). | Predicates and Matching |
string ToBase64(this string text) | Encodes the UTF-8 bytes of the text as Base64. Reverse it with FromBase64. | Encoding and Validation |
string ToBase64(this string text, Encoding encoding) | Encodes the text as Base64 using the given encoding. | Encoding and Validation |
string ToBase64Url(this string text) | Encodes the text (UTF-8) as Base64url: URL- and file-name-safe Base64 without padding (RFC 4648 §5). | Encoding and Validation |
string ToBox(this string text) | Draws a box-drawing frame around the text (which may span several lines), with one space of padding on each side. Width is measured in UTF-16 characters, so East Asian wide characters and emoji can misalign the frame. | Tables, Charts and Exports |
string ToBox(this string text, string title) | Draws a box-drawing frame around the text (which may span several lines), with one space of padding on each side and title embedded in the top border. A title wider than the text widens the box. Width is measured in UTF-16 characters, so East Asian wide characters and emoji can misalign the frame. | Tables, Charts and Exports |
long ToByteCount(this string text) | Parses a human size such as "10 MB", "1.5GB", "512 bytes" or "4 KiB" into bytes, using 1024-based units (the reverse of ToFileSize()). A number without a unit is bytes. | Parsing |
string? ToCamelCase(this string? str, bool useCurrentCulture = false) | Converts text to camelCase. Example: "HTTP response code" → "httpResponseCode". | Casing and Slugs |
string? ToCamelCase(this string? str, bool useCurrentCulture, bool handleAbbreviations) | Converts text to camelCase. | Casing and Slugs |
T? ToClosestEnum<T>(this string? text) | Finds the enum member whose name — or DescriptionAttribute text — is closest to the text, ignoring case; a typo-tolerant ToEnum for user input, query strings and configuration files. Uses the same scoring as ClosestMatch. | Fuzzy and Natural Sort |
T? ToClosestEnum<T>(this string? text, double minSimilarity) | Finds the enum member whose name — or DescriptionAttribute text — is closest to the text, ignoring case. | Fuzzy and Natural Sort |
string? ToConstantCase(this string? str, bool useCurrentCulture = false) | Converts text to CONSTANT_CASE (screaming snake case). Example: "maxRetryCount" → "MAX_RETRY_COUNT". | Casing and Slugs |
T ToEnum<T>(this string value) | Parses the string as an enum value (by name or number, case-sensitive). | Parsing |
T ToEnum<T>(this string value, bool ignoreCase) | Parses the string as an enum value (by name or number). | Parsing |
string ToHmacSha256(this string payload, string secret) | Computes the HMAC-SHA256 of the text (UTF-8) with secret, as lower-case hex — the format most webhook providers send. | Encoding and Validation |
string? ToKebabCase(this string? str, bool useCurrentCulture = false) | Converts text to kebab-case. Example: "HTMLParser" → "html-parser". | Casing and Slugs |
string ToMd5(this string str) | Computes the MD5 hash of the UTF-8 bytes, as upper-case hex. Use only for checksums and cache keys — MD5 is not safe for passwords or signatures. | Encoding and Validation |
string? ToPascalCase(this string? str, bool useCurrentCulture = false) | Converts text to PascalCase. Example: "http_response code" → "HttpResponseCode". | Casing and Slugs |
string ToQuantity(this string word, int count) | Combines a count with the right form of the word: "1 item", "3 items", "0 people". | Text and Humanizing |
string? ToSentenceCase(this string? str, bool useCurrentCulture = false) | Converts text to a sentence. Example: "ThisIsSampleSentence" → "This is sample sentence". | Casing and Slugs |
string ToSha256(this string str) | Computes the SHA-256 hash of the UTF-8 bytes, as upper-case hex. | Encoding and Validation |
string ToSlug(this string str, int? maxLength = null) | Builds a URL-friendly slug: lower-case, accents removed, words joined by '-'. Letters from any script are kept, so non-Latin titles still produce readable slugs. | Casing and Slugs |
string? ToSnakeCase(this string? str, bool useCurrentCulture = false) | Converts text to snake_case. Example: "ThisIsSampleSentence" → "this_is_sample_sentence". | Casing and Slugs |
Stream ToStream(this string text) | Puts the UTF-8 bytes of the text in a readable MemoryStream positioned at the start. The caller owns the stream and should dispose it. | Encoding and Validation |
Stream ToStream(this string text, Encoding encoding) | Puts the encoded bytes of the text in a readable MemoryStream positioned at the start. The caller owns the stream and should dispose it. | Encoding and Validation |
TimeSpan ToTimeSpan(this string text) | Parses a human duration: "1d 2h 30m", "90s", "500ms", "1.5 hours", "2 weeks", "1 day, 2 hours and 30 minutes", "-5m". Units: ms, s, m, h, d, w and their long forms. Reads the output of TimeSpan.ToHumanString() back. | Parsing |
string? ToTitleCase(this string? str, bool useCurrentCulture = false) | Converts text to Title Case. Example: "user_first_name" → "User First Name". | Casing and Slugs |
string? ToTrainCase(this string? str, bool useCurrentCulture = false) | Converts text to Train-Case, as used by HTTP headers. Example: "contentType" → "Content-Type". | Casing and Slugs |
string TrimLines(this string text) | Removes the trailing whitespace from every line, keeping the line endings. | Manipulation and Layout |
string? Truncate(this string? str, int maxLength) | Cuts the string to at most maxLength characters, keeping the start. | Manipulation and Layout |
string? TruncateFromBeginning(this string? str, int maxLength) | Cuts the string to at most maxLength characters, keeping the end. | Manipulation and Layout |
string? TruncateWithPostfix(this string? str, int maxLength) | Cuts the string to at most maxLength characters and ends it with "..." when cut. The result is never longer than maxLength. | Manipulation and Layout |
string? TruncateWithPostfix(this string? str, int maxLength, string postfix) | Cuts the string to at most maxLength characters and ends it with postfix when cut. The result is never longer than maxLength. | Manipulation and Layout |
string TruncateWords(this string text, int maxWords) | Keeps the first maxWords words and adds "..." when text was cut — for previews and cards. Never cuts inside a word. | Text and Humanizing |
T? TryParse<T>(this string? text) | Parses the text into T, returning null instead of throwing when it can't. Works for numbers, bool, Guid, DateTime, DateTimeOffset, TimeSpan, char, enums (by name or number, case-insensitive; numbers must be defined members, or a valid combination for [Flags]) and any value type with a TypeConverter. Uses the invariant culture: '.' is the decimal separator and thousands separators are rejected ("1,5" returns null, not 15). Surrounding white space is ignored. Integers accept only decimal digits with an optional leading sign (no hex, no trailing sign); decimal does not accept a trailing sign or an exponent. Non-[Flags] enums accept a single name or number only. DateTime: text with an offset or 'Z' returns a UTC value (DateTimeKind.Utc); text without an offset returns DateTimeKind.Unspecified. DateTimeOffset: text without an offset is read as UTC (+00:00). Results never depend on the server's time zone. | Parsing |
bool VerifyHmacSha256(this string payload, string secret, string? signature) | Checks a webhook signature: computes the HMAC-SHA256 of the payload and compares it with signature in constant time (so attackers cannot guess it byte by byte from response times). Accepts hex in any case, Base64, and an optional sha256= prefix (GitHub style). | Encoding and Validation |
int WordCount(this string text) | Counts the words: runs of non-space characters that contain at least one letter or digit. | Text and Humanizing |
string Wrap(this string text, int width) | Word-wraps the text so no line is longer than width characters. Existing line breaks are kept; a word longer than the width is split. | Text and Humanizing |
extension(string?) static string operator *(string value, int count) | Repeats the text: "ab" * 3 is "ababab" and "-" * 40 draws a rule. | C# 14 Extension Members |
TaskOutcome<T>
How one task ended: its result, or the exception that stopped it. Returned by WhenAllSettled().
Namespace: System.Threading.Tasks. Guide: Tasks and Resilience.
| Member | Summary | Guide |
|---|---|---|
Exception? Error { get; } | Gets the exception for a failed task; null on success. A cancelled task reports an OperationCanceledException — often its TaskCanceledException subclass, but not always (an async method that calls ThrowIfCancellationRequested reports the base type), so test for is OperationCanceledException. A faulted task reports its first exception. | Tasks and Resilience |
bool IsSuccess { get; } | Gets a value indicating whether the task completed successfully. | Tasks and Resilience |
T? Value { get; } | Gets the result; default when the task failed. | Tasks and Resilience |
TaskTupleExtensions
Lets you await a tuple of tasks. They run concurrently, and the results come back as a tuple. It is Task.WhenAll with typed results. If a task fails, the await throws its exception.
Namespace: System.Threading.Tasks.
| Member | Summary | Guide |
|---|---|---|
TaskAwaiter GetAwaiter(this (Task, Task, Task, Task, Task) tasks) | Awaits five tasks without results concurrently. | Await Anything |
TaskAwaiter GetAwaiter(this (Task, Task, Task, Task) tasks) | Awaits four tasks without results concurrently. | Await Anything |
TaskAwaiter GetAwaiter(this (Task, Task, Task) tasks) | Awaits three tasks without results concurrently. | Await Anything |
TaskAwaiter GetAwaiter(this (Task, Task) tasks) | Awaits two tasks without results concurrently. | Await Anything |
TaskAwaiter<(T1, T2, T3, T4, T5)> GetAwaiter<T1, T2, T3, T4, T5>(this (Task<T1>, Task<T2>, Task<T3>, Task<T4>, Task<T5>) tasks) | Awaits five tasks concurrently and returns all results. | Await Anything |
TaskAwaiter<(T1, T2, T3, T4)> GetAwaiter<T1, T2, T3, T4>(this (Task<T1>, Task<T2>, Task<T3>, Task<T4>) tasks) | Awaits four tasks concurrently and returns all results. | Await Anything |
TaskAwaiter<(T1, T2, T3)> GetAwaiter<T1, T2, T3>(this (Task<T1>, Task<T2>, Task<T3>) tasks) | Awaits three tasks concurrently and returns all results. | Await Anything |
TaskAwaiter<(T1, T2)> GetAwaiter<T1, T2>(this (Task<T1>, Task<T2>) tasks) | Awaits two tasks concurrently and returns both results. | Await Anything |
TextReaderExtensions
Lets foreach and await foreach read a TextReader line by line. Lines are read lazily, so large files are never loaded at once. The reader is not disposed.
Namespace: System.IO.
| Member | Summary | Guide |
|---|---|---|
IAsyncEnumerator<string> GetAsyncEnumerator(this TextReader reader, CancellationToken cancellationToken = default) | Asynchronously enumerates the remaining lines of the reader. | foreach and Deconstruction |
IEnumerator<string> GetEnumerator(this TextReader reader) | Enumerates the remaining lines of the reader. | foreach and Deconstruction |
TimeSpanExtensions
Fluent TimeSpan creation: 5.Minutes(), 1.5.Hours(), 2.Days().Before(now).
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
DateTime After(this TimeSpan span, DateTime moment) | Gets the moment that is this long after moment: 30.Minutes().After(now). | Durations |
DateTimeOffset After(this TimeSpan span, DateTimeOffset moment) | Gets the moment that is this long after moment. | Durations |
DateTime Before(this TimeSpan span, DateTime moment) | Gets the moment that is this long before moment: 2.Hours().Before(now). | Durations |
DateTimeOffset Before(this TimeSpan span, DateTimeOffset moment) | Gets the moment that is this long before moment. | Durations |
TimeSpan CeilingTo(this TimeSpan value, TimeSpan interval) | Rounds toward positive infinity to a multiple of interval: 100s → 2min, -100s → -1min. | Durations |
TimeSpan Days(this double value) | Creates a TimeSpan of value days. | Durations |
TimeSpan Days(this int value) | Creates a TimeSpan of value days. | Durations |
void Deconstruct(this TimeSpan span, out int hours, out int minutes, out int seconds) | Deconstructs into hours, minutes and seconds. Whole days are folded into the hours (1.02:30:00 → (26, 30, 0)), so the three parts always describe the whole duration to the second. Negative durations give negative parts. | foreach and Deconstruction |
void Deconstruct(this TimeSpan span, out int days, out int hours, out int minutes, out int seconds) | Deconstructs into days, hours, minutes and seconds (1.02:30:00 → (1, 2, 30, 0)). Negative durations give negative parts. | foreach and Deconstruction |
TimeSpan FloorTo(this TimeSpan value, TimeSpan interval) | Rounds toward negative infinity to a multiple of interval: 100s → 1min, -100s → -2min. | Durations |
extension(TimeSpan) static TimeSpan FromWeeks(double weeks) | Creates a duration from a number of weeks, the one factory TimeSpan is missing: TimeSpan.FromWeeks(2) is 14 days. | C# 14 Extension Members |
TimeSpan Hours(this double value) | Creates a TimeSpan of value hours. | Durations |
TimeSpan Hours(this int value) | Creates a TimeSpan of value hours. | Durations |
TimeSpan Milliseconds(this double value) | Creates a TimeSpan of value milliseconds. | Durations |
TimeSpan Milliseconds(this int value) | Creates a TimeSpan of value milliseconds. | Durations |
TimeSpan Minutes(this double value) | Creates a TimeSpan of value minutes. | Durations |
TimeSpan Minutes(this int value) | Creates a TimeSpan of value minutes. | Durations |
TimeSpan RoundTo(this TimeSpan value, TimeSpan interval) | Rounds to the nearest multiple of interval; a midpoint rounds away from zero, so negative durations round symmetrically (-90s → -2min, +90s → +2min). | Durations |
TimeSpan Seconds(this double value) | Creates a TimeSpan of value seconds. | Durations |
TimeSpan Seconds(this int value) | Creates a TimeSpan of value seconds. | Durations |
string ToHumanString(this TimeSpan span, int maxUnits = 2147483647) | Describes the duration in words: "1 day, 2 hours, 30 minutes". Zero is "0 seconds"; negative durations start with "minus". "...".ToTimeSpan() reads it back. | Durations |
TimeSpan Weeks(this double value) | Creates a TimeSpan of value weeks. | Durations |
TimeSpan Weeks(this int value) | Creates a TimeSpan of value weeks. | Durations |
TypeExtensions
Extension methods for Type.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
object? GetDefaultValue(this Type type) | Gets what default(T) would be: null for reference and nullable types, a zeroed instance for other value types. | Types and Reflection |
string GetFriendlyName(this Type type) | Gets the type name as you would write it in C#, for logs and error messages: Dictionary<string, List<int>> instead of Dictionary`2, int?, string[], (int, string), and Outer.Inner for nested types. | Types and Reflection |
string GetFullNameWithAssemblyName(this Type type) | Gets "Full.Type.Name, AssemblyName" — the short assembly-qualified name, without version or key. | Types and Reflection |
Type[]? GetGenericArgumentsOf(this Type type, Type openGenericType) | Gets the type arguments the type supplies to an open generic type it is, inherits from, or implements. The type and its base classes are searched first, then its interfaces; the first match wins. | Types and Reflection |
Type[] GetInterfacesAndAbstractClasses(this Type type) | Gets every interface the type implements plus its abstract base classes. | Types and Reflection |
bool IsAnonymous(this Type type) | Checks whether the type is a compiler-generated anonymous type (new { Name = "x" }). | Types and Reflection |
bool IsAssignableTo<TTarget>(this Type type) | Checks whether a value of this type can be assigned to a TTarget. | Types and Reflection |
bool IsAssignableTo(this Type type, Type targetType) | Checks whether a value of this type can be assigned to targetType. | Types and Reflection |
bool IsAssignableToGenericType(this Type? givenType, Type? genericType) | Checks whether the type is, inherits from, or implements an open generic type such as IRepository<>. | Types and Reflection |
bool IsComplex(this Type type) | Checks whether the type is "complex" — not a primitive, enum, string, decimal, date/time, Guid or Uri (nullable versions included). Complex values have properties of their own. | Types and Reflection |
bool IsNullableValueType(this Type type) | Checks whether the type is a Nullable. | Types and Reflection |
bool IsNumeric(this Type type) | Checks whether the type is numeric: the integer and floating-point primitives, decimal, Half, Int128 and UInt128, or a Nullable of one of them. Enums, bool and char are not numeric. | Types and Reflection |
bool IsRecord(this Type type) | Checks whether the type is a record class or a record struct. | Types and Reflection |
bool IsStatic(this Type type) | Checks whether the type is a static class (abstract and sealed). | Types and Reflection |
Type UnwrapNullable(this Type type) | Gets T for a Nullable, or the type itself otherwise. | Types and Reflection |
UnixTimeExtensions
Static extension members for DateTime (C# 14). They live in their own class because an extension member compiles to a static method, and FromUnixTimeSeconds(long) already exists on DateTimeExtensions as the long extension.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
extension(DateTime) static DateTime FromUnixTimeMilliseconds(long milliseconds) | Converts Unix time in milliseconds to a UTC DateTime: DateTime.FromUnixTimeMilliseconds(1715774400000). | C# 14 Extension Members |
extension(DateTime) static DateTime FromUnixTimeSeconds(long seconds) | Converts Unix time in seconds to a UTC DateTime, the way DateTimeOffset.FromUnixTimeSeconds does for DateTimeOffset: DateTime.FromUnixTimeSeconds(1715774400). | C# 14 Extension Members |
UriExtensions
Builds URLs safely: path segments and query values are always percent-encoded, slashes are handled for you.
Namespace: System.
| Member | Summary | Guide |
|---|---|---|
Uri Append(this Uri uri, params object?[] segments) | Adds path segments to an absolute URL. Each segment is percent-encoded, so "a/b" stays one segment (a%2Fb) and user input cannot change the path. Numbers, dates and other IFormattable values use the invariant culture; a value written through its own ToString() (a type that is not IFormattable) uses whatever culture that method uses, so keep such a ToString() culture-independent (a ToString() that formats a decimal with the current culture gives 1%2C5 under de-DE and 1.5 under en-US). A collection argument (an array or list, but not a string) adds one segment per item, and each item follows the same rules. An existing query string and fragment are kept. Each segment (or collection item) must be a value that formats as meaningful text: a string, number, enum, bool, char, Guid, date/time value or Uri; any other IFormattable value (such as BigInteger, written with the invariant culture), including one whose type a source generator marked [CompilerGenerated]; a Version or IPAddress; or a type of your own (outside the System and Microsoft namespaces) that declares a hand-written ToString() override (such as a strongly-typed ID record). Everything else throws, because its ToString() would become a noise or sensitive segment: an exception, even your own subclass (its message), a Type or other reflection object, a delegate, a Task, Memory<T>, ReadOnlyMemory<T>, Lazy<T>, any other framework type that is not IFormattable (such as StringBuilder), a KeyValuePair or DictionaryEntry ([a, 1]), a tuple, an anonymous object, a record that keeps the compiler-generated ToString() (OrderId { Value = 42 }), or a type that inherits ToString() from object (its type name). This is also why a dictionary argument throws. Calling Append() with no segments at all (an empty segments array) returns uri unchanged, without adding a trailing slash. Missing values are never skipped: a null, empty or whitespace-only segment throws, because dropping it would silently change which resource the URL points to (users/{id}/orders would become users/orders). | IO and System |
Uri WithQuery(this Uri uri, object parameters) | Sets query parameters with "with" semantics: a parameter the URL already has with the same name (case-sensitive) is replaced (all its old values are removed and the new values are added at the end); unrelated parameters keep their values and order; new names are added at the end. Keys and values are percent-encoded, with the same formatting rules as ToQueryString(). parameters can be: an object, whose public properties become parameters exactly as object.ToQueryString() writes them (nested objects become Address.City=...); any IDictionary (Dictionary<string, string>, Dictionary<string, object?>, Hashtable, ...); or any IEnumerable<KeyValuePair<string, TValue>>, where a key may appear more than once. Dictionary and pair keys and values are formatted alike: numbers, dates and other IFormattable values use the invariant culture. Values must be simple values or collections of simple values; any other key or value is written with its ToString(), which may depend on the current culture. null values are skipped, so a null does not remove an existing parameter. A collection value repeats the key (ids=1&ids=2). To set raw query text, use UriBuilder.Query. | IO and System |