Skip to main content

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​

TopicWhat it covers
Add the packagedotnet add package, PackageReference, central package management
Supported targetsWhich build (netstandard2.1 or net10.0) your app gets
DependenciesWhat the package pulls in: only System.Text.Json, only on netstandard2.1
Compiler requirementsClassic extension methods versus the C# 14 member syntax
No using neededWhy the methods appear without importing a namespace
Resolving name clashesStatic calls and using aliases
Check that it worksA two-line smoke test

Add the package​

.NET CLI​

Run this in the folder that holds your project file:

Terminal
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.

Orders.Api.csproj
<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.

Directory.Packages.props
<ItemGroup>
<PackageVersion Include="Masterly.Extensions.Core" Version="2.0.0" />
</ItemGroup>
Orders.Api.csproj
<ItemGroup>
<PackageReference Include="Masterly.Extensions.Core" />
</ItemGroup>
Upgrading from 1.x?

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 targetsBuild it runs
netcoreapp3.0, netcoreapp3.1netstandard2.1
net5.0 to net9.0netstandard2.1
net10.0 or laternet10.0
Any other platform that implements .NET Standard 2.1netstandard2.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

.NET Framework 4.x does not implement .NET Standard 2.1, so a .NET Framework project cannot reference this package.

Trimming and Native AOT

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.

BuildPackage dependencies
net10.0None. JSON members use the System.Text.Json that ships with .NET.
netstandard2.1System.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:

ExtensionMembers.cs
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:

Orders.Api.csproj
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
note

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:

ClassicEquivalents.cs
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.

InvoiceService.cs
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:

NamespaceExtensions in it
SystemStrings, objects, numbers, dates, durations, enums, types, Guids, URIs, byte arrays, exceptions, functions, disposables, Random, Range
System.Collections.GenericSequences, collections, lists, dictionaries, collection initializers, Page<T>
System.Collections.SpecializedNameValueCollection
System.LinqIQueryable<T> and IGrouping<TKey, TElement>
System.Threading.TasksTasks, awaitables and Retry
System.ThreadingSemaphoreSlim
System.IOStream and TextReader
System.DiagnosticsProcess
System.GlobalizationCultureInfo
System.ReflectionGetPropertyValue / SetPropertyValue, and GetAttribute<T> / GetAttributes<T> / HasAttribute<T> on MemberInfo
note

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:

LibraryNames that can clash
FluentAssertionsAs<T>()
System.Interactive (Ix)Scan, DistinctUntilChanged, IsEmpty
Your own codethe 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:

RevenueReport.cs
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:

AnimalTests.cs
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.

OrderEndpoints.cs
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.

Program.cs
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​