FormatWith 3.0.1
dotnet add package FormatWith --version 3.0.1
NuGet\Install-Package FormatWith -Version 3.0.1
<PackageReference Include="FormatWith" Version="3.0.1" />
paket add FormatWith --version 3.0.1
#r "nuget: FormatWith, 3.0.1"
// 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
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 | Versions 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. |
-
.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.