Installation
Masterly.Extensions.Core is one NuGet package with no third-party dependencies. This page shows how to add it, which build your app runs, which compiler you need, and what to do when one of its method names clashes with another library.
At a glance
| Topic | What it covers |
|---|---|
| Add the package | dotnet add package, PackageReference, central package management |
| Supported targets | Which build (netstandard2.1 or net10.0) your app gets |
| Dependencies | What the package pulls in: only System.Text.Json, only on netstandard2.1 |
| Compiler requirements | Classic extension methods versus the C# 14 member syntax |
No using needed | Why the methods appear without importing a namespace |
| Resolving name clashes | Static calls and using aliases |
| Check that it works | A two-line smoke test |
Add the package
.NET CLI
Run this in the folder that holds your project file:
dotnet add package Masterly.Extensions.Core
The command adds the latest stable version to the project file and restores it.
PackageReference
You can also edit the project file yourself. 2.0.0 is the current major version. Check nuget.org for the newest release.
<ItemGroup>
<PackageReference Include="Masterly.Extensions.Core" Version="2.0.0" />
</ItemGroup>
Central package management
If your solution keeps package versions in Directory.Packages.props, put the version there and leave it out of the project file.
<ItemGroup>
<PackageVersion Include="Masterly.Extensions.Core" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Masterly.Extensions.Core" />
</ItemGroup>
Version 2.0 fixes many bugs and changes some behavior, the target frameworks and two class names. Read Upgrading from 1.x before you bump the version.
Supported targets
The package ships two builds:
- netstandard2.1: used by every .NET Core 3.0+ app and every .NET 5 to .NET 9 app.
- net10.0: an optimized build for .NET 10 apps. It uses newer runtime APIs where they are faster, and it is marked trim- and AOT-compatible.
NuGet picks the build for you. You do not choose it.
| Your app targets | Build it runs |
|---|---|
netcoreapp3.0, netcoreapp3.1 | netstandard2.1 |
net5.0 to net9.0 | netstandard2.1 |
net10.0 or later | net10.0 |
| Any other platform that implements .NET Standard 2.1 | netstandard2.1 |
Both builds expose the same public API. The library's test suite runs every test twice: on .NET 8, which exercises the netstandard2.1 build, and on .NET 10.
.NET Framework 4.x does not implement .NET Standard 2.1, so a .NET Framework project cannot reference this package.
On the net10.0 build, the few members that rely on reflection or on reflection-based JSON carry [RequiresUnreferencedCode] / [RequiresDynamicCode]. A trimmed or AOT-published app gets a warning exactly at those calls. See Trimming and AOT.
Dependencies
The package has no third-party dependencies. The 1.x dependencies (Ardalis.GuardClauses, JetBrains.Annotations and Newtonsoft.Json) are gone.
| Build | Package dependencies |
|---|---|
| net10.0 | None. JSON members use the System.Text.Json that ships with .NET. |
| netstandard2.1 | System.Text.Json 10.0.14, because .NET Standard 2.1 has no built-in JSON serializer. |
JSON-based members such as ToJsonString, FromJson, DeepClone and ToSafeJson use System.Text.Json on both builds.
Compiler requirements
Classic extension methods
Almost every member is a classic x.Method() extension method. These compile to ordinary static methods and work with the compiler your target framework uses by default. A few features rely on syntax you need anyway: foreach (int i in 0..5) uses ranges, which arrived in C# 8, the default for .NET Core 3.x.
C# 14 extension members
A few members are C# 14 extension properties, static members and operators. They read as if the BCL type declared them:
string? customerName = " ";
bool missing = customerName.IsBlank; // → true
string rule = "-" * 40; // → "----------------------------------------"
string pattern = "ab" * 3; // → "ababab"
TimeSpan trialPeriod = TimeSpan.FromWeeks(2); // → 14.00:00:00
string orderId = Guid.NewShortId(); // → a new 22-character URL-safe id
Dictionary<string, string> defaults = new Dictionary<string, string> { ["theme"] = "light", ["lang"] = "en" };
Dictionary<string, string> overrides = new Dictionary<string, string> { ["theme"] = "dark" };
Dictionary<string, string> settings = defaults + overrides;
// → { ["theme"] = "dark", ["lang"] = "en" }
These members also compile to static methods, so the library still runs on every supported runtime. Only the syntax needs a C# 14 compiler: the .NET 10 SDK or Visual Studio 2026. With an older language version, the compiler reports a "feature is not available" error when it meets the new syntax: CS9260 on C# 13, CS9202 on C# 12, CS9058 on C# 11, CS8936 on C# 10, CS8773 on C# 9 and CS8400 on C# 8. Every classic extension method keeps working.
The default language version depends on the target framework, not on the SDK. A net10.0 project gets C# 14 by default. A project that targets an older framework gets an older default, even when you build it with the .NET 10 SDK. To use the member syntax there, raise the language version:
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
Microsoft does not officially support a language version newer than a target framework's default. Extension members need no runtime support, though, and this library's own tests use the member syntax on net8.0 with LangVersion set to latest.
If you cannot use C# 14, each of these members has a classic equivalent:
string? customerName = " ";
bool missing = customerName.IsNullOrWhiteSpace(); // instead of customerName.IsBlank → true
string rule = "-".Repeat(40); // instead of "-" * 40
string orderId = Guid.NewGuid().ToShortId(); // instead of Guid.NewShortId()
TimeSpan trialPeriod = 2.Weeks(); // instead of TimeSpan.FromWeeks(2)
Dictionary<string, string> defaults = new Dictionary<string, string> { ["theme"] = "light", ["lang"] = "en" };
Dictionary<string, string> overrides = new Dictionary<string, string> { ["theme"] = "dark" };
Dictionary<string, string> settings = defaults.Merge(overrides); // instead of defaults + overrides
The full list is on C# 14 extension members.
No using needed
Each extension class lives in the namespace of the type it extends, not in a Masterly.* namespace. String extensions are in System, sequence extensions are in System.Collections.Generic, task extensions are in System.Threading.Tasks, and so on. You already import those namespaces to use the types, so the methods appear in IntelliSense without an extra using.
using System;
using System.Collections.Generic;
// No "using Masterly..." line is needed.
string? routeName = "getHTTPResponse".ToKebabCase(); // → "get-http-response"
IEnumerable<int> runningTotals = new[] { 1, 2, 3, 4 }.Scan((sum, item) => sum + item);
// → [1, 3, 6, 10]
In an SDK-style project with <ImplicitUsings>enable</ImplicitUsings> (the default for new .NET 6+ projects), System, System.Collections.Generic, System.IO, System.Linq, System.Threading and System.Threading.Tasks are imported globally, so you do not even write the two using lines above.
The other namespaces are the ones you already import to name the type:
| Namespace | Extensions in it |
|---|---|
System | Strings, objects, numbers, dates, durations, enums, types, Guids, URIs, byte arrays, exceptions, functions, disposables, Random, Range |
System.Collections.Generic | Sequences, collections, lists, dictionaries, collection initializers, Page<T> |
System.Collections.Specialized | NameValueCollection |
System.Linq | IQueryable<T> and IGrouping<TKey, TElement> |
System.Threading.Tasks | Tasks, awaitables and Retry |
System.Threading | SemaphoreSlim |
System.IO | Stream and TextReader |
System.Diagnostics | Process |
System.Globalization | CultureInfo |
System.Reflection | GetPropertyValue / SetPropertyValue, and GetAttribute<T> / GetAttributes<T> / HasAttribute<T> on MemberInfo |
GetPropertyValue and SetPropertyValue extend object, but they live in System.Reflection. They appear on every object only after you add using System.Reflection;. That keeps reflection-based helpers out of IntelliSense until you ask for them.
Resolving name clashes
Method names avoid clashing with the LINQ methods .NET itself ships, for example .NET 9's Index and .NET 10's Shuffle. A few names do exist in other popular libraries:
| Library | Names that can clash |
|---|---|
| FluentAssertions | As<T>() |
| System.Interactive (Ix) | Scan, DistinctUntilChanged, IsEmpty |
| Your own code | the Page<T> type, if your project defines one |
When the compiler reports an ambiguous call (error CS0121) or an ambiguous type name (error CS0104), you have two options.
Call the method statically
An extension method is a static method. Call it through its class and pass the receiver as the first argument:
decimal[] dailyRevenue = { 120m, 80m, 200m };
// Ambiguous when System.Interactive is also referenced:
// IEnumerable<decimal> cumulative = dailyRevenue.Scan((sum, amount) => sum + amount);
IEnumerable<decimal> cumulative = EnumerableExtensions.Scan(dailyRevenue, (sum, amount) => sum + amount);
// → [120, 200, 400]
The extension classes are named after the type they extend: StringExtensions, EnumerableExtensions, DictionaryExtensions, ObjectExtensions, DateTimeExtensions, and so on. Task helpers are in AsyncExtensions, not TaskExtensions, to avoid a BCL name.
Use a using alias
An alias gives a class or type a name that cannot clash. This helps with As<T>(), which both FluentAssertions and this library define on object. It also helps with the two classes this library names ObjectExtensions: one in System, one in System.Reflection. A file that imports System.Reflection sees both, so the plain class name is ambiguous there:
using System;
using System.Reflection;
using FluentAssertions;
using SystemObjectExtensions = System.ObjectExtensions;
object value = new Dog();
// Ambiguous when FluentAssertions is also imported:
// Animal animal = value.As<Animal>();
Animal animal = SystemObjectExtensions.As<Animal>(value); // → the same instance, typed as Animal
For a type clash such as Page<T>, alias a closed generic type. C# aliases cannot be open generics.
using System.Collections.Generic;
using OrderPage = System.Collections.Generic.Page<OrderSummary>;
List<OrderSummary> orders = LoadOrderSummaries();
OrderPage firstPage = orders.ToPage(pageNumber: 1, pageSize: 20);
Check that it works
Build and run two calls. If the first one compiles, the package is installed and its namespaces are in scope.
string? routeName = "getHTTPResponse".ToKebabCase(); // → "get-http-response"
string pattern = "ab" * 3; // → "ababab" (needs C# 14)
If the first line compiles but the second fails with a "Feature 'extensions' is not available in C# N" error (CS9260 on C# 13, CS9202 on C# 12, and so on), your project uses a language version older than C# 14. See Compiler requirements.
See also
- Quick start: the most useful calls in a few minutes
- Conventions: null handling, culture and the "no clock" rule
- C# 14 extension members: properties, static members and operators on BCL types
- Upgrading from 1.x: breaking changes in 2.0