Skip to main content
v2.0.0is out: upgrading from 1.x

Extension methods that feel built in.

A dependency-free library of 558 extension methods for .NET that live in the BCL namespaces, so they appear on your strings, lists and tasks without a single using.

Masterly.Extensions.Core logo: a value, a member-access dot and a pair of parentheses
  • "getHTTPResponse".ToKebabCase()returns "get-http-response"
  • "box".ToQuantity(2)returns "2 boxes"
  • "1d 2h 30m".ToTimeSpan()returns 1.02:30:00
  • 2024.ToRoman()returns "MMXXIV"

Install from NuGet​

dotnet add package Masterly.Extensions.Core
Installation guide
See it run

A latency report, straight to the console​

One outlier turns a 114 ms p95 into 1,104 ms. WithoutOutliers, Percentile, ToSparkline, ToBarChart and ToTable show it in the console, with no charting or table package.

LatencyReport.csnet10.0
double[] latencies = [118, 92, 104, 97, 2310, 101, 95, 99, 110, 93, 106, 98];
double[] typical = latencies.WithoutOutliers().ToArray();

Console.WriteLine($"raw {latencies.ToSparkline(),-12} p95 {latencies.Percentile(95):0} ms");
Console.WriteLine($"clean {typical.ToSparkline(),-12} p95 {typical.Percentile(95):0} ms");

Endpoint[] endpoints =
[
new("GET /orders", 1240, 104),
new("GET /users", 655, 97),
new("POST /orders", 410, 231),
];

Console.WriteLine(endpoints.ToBarChart(e => e.Route, e => e.Calls, 20));
Console.WriteLine(endpoints.ToTable());

record Endpoint(string Route, int Calls, int P95);
Console outputexit code 0
dotnet runraw    ▁▁▁▁█▁▁▁▁▁▁▁  p95 1104 msclean  █▁▄▂▃▂▃▆▁▅▃   p95 114 msGET /orders   ████████████████████  1,240GET /users    ██████████▋           655POST /orders  ██████▋               410┌──────────────┬───────┬─────┐│ Route        │ Calls │ P95 │├──────────────┼───────┼─────┤│ GET /orders  │  1240 │ 104 ││ GET /users   │   655 │  97 ││ POST /orders │   410 │ 231 │└──────────────┴───────┴─────┘

This is the real output of the program, run on .NET 10. Console output guide

  • 0third-party dependencies. Only System.Text.Json, and only on netstandard2.1.
  • netstandard2.1 + net10.0Any .NET Core 3.0+ or .NET 5+ app, with a tuned .NET 10 build.
  • Trim + AOTAnnotated: warnings appear only where reflection is used.
  • 558public extension methods and members, overloads counted.
Language

C# that reads like the framework​

C# binds await, foreach, deconstruction and query syntax to methods by name, so an extension method can switch them on. The package does that for the types you already use.

C# awaits any value that has a GetAwaiter method, and an extension method counts. The package adds one to task tuples, task lists, TimeSpan, SemaphoreSlim, Process and CancellationToken. Learn more

(User user, Order[] orders) = await (GetUserAsync(id), GetOrdersAsync(id)); // both at once
string[] pages = await urls.Select(FetchAsync); // a whole list of tasks
await 2.Seconds(); // a delay
using (await _lock) { /* one caller at a time */ } // a SemaphoreSlim
int exitCode = await Process.Start("git", "pull"); // a process, to its exit code
await stoppingToken; // until shutdown is requested
Capabilities

The helpers you keep rewriting, written once​

Every method validates its arguments, carries nullable annotations and is covered by tests, so you can delete the copy in your utilities folder.

Text for people

Plurals, numbers in words, durations and relative times, all in invariant English, so a label reads the same on every server.

Read the guide
"box".ToQuantity(2)
2 boxes
123.ToWords()
one hundred twenty-three
21.ToOrdinal()
21st
90.Minutes().ToHumanString()
1 hour, 30 minutes
now.AddHours(-3).ToRelativeTime(now)
3 hours ago

Read what people type

"10 MB", "1d 2h 30m" and "/users/{id}" templates become numbers, durations and values. TryParse<int>() returns null instead of throwing.

Read the guide

Statistics in one call

Percentile, Median and StandardDeviation for int, long, double and decimal, plus WithoutOutliers before you report.

Read the guide

Diffs and build order

DiffWith splits two lists into added, removed, changed and unchanged by key. TopologicalSort orders dependencies and names the cycle.

Read the guide

Paging and safe sorting

ToPage returns a Page<T> with totals. SortBy reads a ?sort= string and, given an allow-list, sorts only by the properties you name. Both run in SQL on IQueryable.

Read the guide

Did you mean?

ClosestMatch suggests "status" for "statsu", ToClosestEnum forgives typos, and OrderByNatural puts file2 before file10.

Read the guide

Logs that keep secrets

ToSafeJson replaces passwords, tokens, API keys and card numbers with "***" at any depth, so a request or an entity can go straight into a log.

Read the guide
account.ToSafeJson(){"Name":"Ahmad","Email":"ahmad@example.com","Password":"***","ApiToken":"***"}

Compare, clone and map

CompareProperties for audit trails, DeepClone, MapTo for DTOs and UpdateFrom for PATCH, without a mapping library.

Read the guide

Functions with manners

Memoize, Debounce, Throttle and SingleFlight wrap a delegate and return one you call as before.

Read the guide

Trim and AOT aware

The .NET 10 build is marked trim- and AOT-compatible. Reflection-based members say so with a warning at the call site.

Read the guide
Areas

Six areas, one package​

Start from the area you need. Each method name opens the guide that explains it.

Write less glue code today.​

The Quick Start takes one console app through strings, parsing, collections, tasks, objects, time and console output in seven short steps.