FormatWith 3.0.1

dotnet add package FormatWith --version 3.0.1                
NuGet\Install-Package FormatWith -Version 3.0.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FormatWith" Version="3.0.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add FormatWith --version 3.0.1                
#r "nuget: FormatWith, 3.0.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install FormatWith as a Cake Addin
#addin nuget:?package=FormatWith&version=3.0.1

// Install FormatWith as a Cake Tool
#tool nuget:?package=FormatWith&version=3.0.1                

FormatWith

NuGet license

A set of string extension methods for performing {named} {{parameterized}} string formatting, written for NetStandard 2.0.

Quick Info

This library provides named string formatting via the string extension .FormatWith(). It formats strings against a lookup dictionary, anonymous type, or handler.

It is written as a Net Standard 2.0 class library, published as a NuGet package, and is fully compatible with any .NET platform that implements NetStandard 2.0. This makes it compatible with .NET Core 2.0, .NET Full Framework 4.6.1, UWP/UAP 10, and most mono/xamarin platforms.

An example of what it can do:

using FormatWith;
...
string formatString = "Your name is {name}, and this is {{escaped}}, this {{{works}}}, and this is {{{{doubleEscaped}}}}";

// format the format string using the FormatWith() string extension.
// We can parse in replacement parameters as an anonymous type
string output = formatString.FormatWith(new { name = "John", works = "is good" });

// output now contains the formatted text.
Console.WriteLine(output);

Produces:

"Your name is John, and this is {escaped}, this {is good}, and this is {{doubleEscaped}}"

It can also be fed parameters via an IDictionary<string, string> or an IDictionary<string, object>, rather than a type.

The value of each replacement parameter is given by whatever the objects .ToString() method produces. This value is not cached, so you can get creative with the implementation (the object is fed directly into a StringBuilder).

How it works

A state machine parser quickly runs through the input format string, tokenizing the input into tokens of either "normal" or "parameter" text. These tokens are simply a struct with an index and length into the original format string - SubString() is avoided to prevent unnecessary string allocations. These are fed out of an enumerator right into a StringBuilder. Since StringBuilder is pre-allocated a small chunk of memory, and only .Append()ed relatively large segments of string, it produces the final output string quickly and efficiently.

Extension methods:

Three extension methods for string are defined in FormatWith.StringExtensions: FormatWith(), FormattableWith(), and GetFormatParameters().

FormatWith

The first, second, and third overload of FormatWith() take a format string containing named parameters, along with an object, dictionary, or function for providing replacement parameters. Optionally, missing key behaviour, a fallback value, and custom brace characters can be specified. Two adjacent opening or closing brace characters in the format string are treated as escaped, and will be reduced to a single brace in the output string.

Missing key behaviour is specified by the MissingKeyBehaviour enum, which can be ThrowException, ReplaceWithFallback, or Ignore.

ThrowException throws a KeyNotFoundException if a replacement value for a parameter in the format string could not be found.

ReplaceWithFallback inserts the value specified by fallbackReplacementValue in place of any parameters that could not be replaced. If an object-based overload is used, fallbackReplacementValue is an object, and the string representation of the object will be resolved as the value.

Ignore ignores any parameters that did not have a corresponding key in the lookup dictionary, leaving the unmodified braced parameter in the output string. This is useful for tiered formatting.

Examples:

`string output = "abc {Replacement1} {DoesntExist}".FormatWith(new { Replacement1 = Replacement1, Replacement2 = Replacement2 });

output: Throws a KeyNotFoundException with the message "The parameter "DoesntExist" was not present in the lookup dictionary".

string output = "abc {Replacement1} {DoesntExist}".FormatWith(new { Replacement1 = Replacement1, Replacement2 = Replacement2 }, MissingKeyBehaviour.ReplaceWithFallback, "FallbackValue");

output: "abc Replacement1 FallbackValue"

`string replacement = "abc {Replacement1} {DoesntExist}".FormatWith(new { Replacement1 = Replacement1, Replacement2 = Replacement2 }, MissingKeyBehaviour.Ignore);

output: "abc Replacement1 {DoesntExist}"

Using custom brace characters:

Custom brace characters can be specified for both opening and closing parameters, if required.

string replacement = "abc <Replacement1> <DoesntExist>".FormatWith(new { Replacement1 = Replacement1, Replacement2 = Replacement2 }, MissingKeyBehaviour.Ignore, null,'<','>');

output: "abc Replacement1 <DoesntExist>"

FormattableWith

The first, second, and third overload of FormattableWith() function much the same way that the FormatWith() overloads do. However, FormattableWith returns a FormattableString instead of a string. This allows parameters and composite format string to be inspected, and allows a custom formatter to be used if desired.

Handler overloads

A custom handler can be passed to both FormatWith() and FormattableWith(). The handler is passed the value of each parameter key and format (if applicable). It is responsible for providing a ReplacementResult in response. The ReplacementResult contains the Value which will be substituted, as well as a boolean Success parameter indicating whether the replacement was successful. If Success is false, the MissingKeyBehaviour is followed, as per the other overloads of FormatWith.

This can allow for some neat tricks, and even complex behaviours.

Example:

"{abcDEF123:reverse}, {abcDEF123:uppercase}, {abcDEF123:lowercase}.".FormatWith(
            (parameter, format) =>
            {
                switch (format)
                {
                    case "uppercase":
                        return new ReplacementResult(true, parameter.ToUpper());
                    case "lowercase":
                        return new ReplacementResult(true, parameter.ToLower());
                    case "reverse":
                        return new ReplacementResult(true, new string(parameter.Reverse().ToArray()));
                    default:
                        return new ReplacementResult(false, parameter);
                }
            });

Produces:

"321FEDcba, ABCDEF123, abcdef123."

GetFormatParameters

GetFormatParameters() can be used to get a list of parameter names out of a format string, which can be used for inspecting a format string before performing other actions on it.

Example:

IEnumerable<string> parameters = "{parameter1} {parameter2} {{not a parameter}}".GetFormatParameters();

output: The enumerable will return "parameter1","parameter2" during iteration.

Tests:

A testing project is included that has coverage of most scenarios involving the three extension methods. The testing framework in use is xUnit.

Performance:

The SpeedTest test function performs 1,000,000 string formats, with a format string containing 1 parameter. On a low end 1.3Ghz mobile i7, this completes in around 700ms, giving ~1.4 million replacements per second.

The SpeedTestBigger test performs a more complex replacement on a longer string containing 2 parameters and several escaped brackets, again 1,000,000 times. On the same hardware, this test completed in around 1 seconds.

The SpeedTestBiggerAnonymous test is the same as SpeedTestBigger, but uses the anonymous function overload of FormatWith. It completes in just under 2 seconds. Using the anonymous overload of FormatWith is slightly slower due to reflection overhead, although this is minimised by caching.

So as a rough performance guide, FormatWith will usually manage about 1 million parameter replacements per second on low end hardware.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on FormatWith:

Package Downloads
PegasusInfrastructure

Package Description

TTRPG.Engine

Manage game algorithms in serializable structures. https://github.com/timjen3/ttrpg-engine/blob/main/Readme.md

HashiVaultCs

A .NET library for interacting with HashiCorp Vault. Features are minimal, please read documentation.

Pantry.Mediator.AspNetCore

Repositories for Root Aggregates.

Jfw.Core

Provides services to access data and built-in methods in Jframework.

GitHub repositories (2)

Showing the top 2 popular GitHub repositories that depend on FormatWith:

Repository Stars
enkodellc/blazorboilerplate
Blazor Boilerplate / Starter Template with MudBlazor
nickworonekin/puyotools
Puyo Tools is a collection of tools and libraries used to access the contents of various game files. Although it was initially built to handle files used in Puyo Puyo games, it can handle files used in other games as well.
Version Downloads Last updated
3.0.1 600,701 8/25/2020
3.0.0 8,612 8/24/2020
2.2.1 216,120 12/6/2017
2.2.0 93,025 10/18/2017
2.1.0 5,616 9/14/2017
2.0.3 2,669 8/21/2017
2.0.0-alpha 828 8/18/2017
1.4.1 27,194 7/18/2016
1.4.0 5,207 6/28/2016
1.3.1 5,715 6/2/2016
1.3.0 1,443 6/2/2016
1.2.1 1,381 5/11/2016
1.1.0 1,750 3/24/2016
1.0.0 1,851 3/22/2016

Key:format syntax now supported for FormatWith and FormattableWith.
Add strong signing.