Tharga.Toolkit.Standard 1.15.20

dotnet add package Tharga.Toolkit.Standard --version 1.15.20
                    
NuGet\Install-Package Tharga.Toolkit.Standard -Version 1.15.20
                    
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="Tharga.Toolkit.Standard" Version="1.15.20" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tharga.Toolkit.Standard" Version="1.15.20" />
                    
Directory.Packages.props
<PackageReference Include="Tharga.Toolkit.Standard" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Tharga.Toolkit.Standard --version 1.15.20
                    
#r "nuget: Tharga.Toolkit.Standard, 1.15.20"
                    
#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.
#:package Tharga.Toolkit.Standard@1.15.20
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Tharga.Toolkit.Standard&version=1.15.20
                    
Install as a Cake Addin
#tool nuget:?package=Tharga.Toolkit.Standard&version=1.15.20
                    
Install as a Cake Tool

Tharga.Toolkit.Standard

NuGet Nuget License

.NET Standard 2.0 toolkit with utilities for strings, dates, collections, comparisons, timers, and more.

Installation

dotnet add package Tharga.Toolkit.Standard

Features

String Extensions

// Null/empty helpers
string val = "".NullIfEmpty();         // null
bool empty = "".IsNullOrEmpty();       // true
string safe = ((string)null).IfEmpty("fallback"); // "fallback"

// Random strings (cryptographically secure)
var random = StringExtension.GetRandomString(12, 20);
var pin = 6.RandomString(StringExtension.NumericCharacters); // "482917"

// Base64
var encoded = "hello".ToBase64();      // "aGVsbG8="
var decoded = encoded.FromBase64();    // "hello"

// Truncate
var short = "hello world".Truncate(5); // "hello"

Compare

Deep object comparison that returns a list of differences.

var obj1 = new { Name = "Alice", Age = 30 };
var obj2 = new { Name = "Bob", Age = 30 };

var diffs = obj1.Compare(obj2);
foreach (var diff in diffs)
{
    Console.WriteLine(diff.Message);
}

// Ignore sort order in collections
var diffs2 = list1.Compare(list2, CompareExtensions.CompareMode.IgnoreSortOrder);

DateTime Extensions

// Local formatting
var dateStr = DateTime.UtcNow.ToLocalDateString();       // "2024-01-15"
var timeStr = DateTime.UtcNow.ToLocalTimeString();       // "14:30:00"

// Duration strings (relative time)
var ago = someDate.ToDurationString();                    // "3 hours ago"
var swedish = someDate.ToDurationString(new DurationOptions
{
    StringOptions = DurationStringOptionsExtensions.Get(Language.Sv)
}); // "3 timmar sedan"

// TimeSpan formatting
var span = TimeSpan.FromMinutes(45).ToTimeSpanString();  // "45 minutes"

ManagedTimer

Async timer with interval correction, skip detection, and rich events.

var timer = new ManagedTimer(
    TimeSpan.FromSeconds(5),
    async iteration => { /* your work */ },
    autoStart: true
);

timer.BeforeExecuteEvent += (s, e) => { /* can cancel via e.Cancel = true */ };
timer.AfterExecuteEvent += (s, e) => { /* check e.Exception, e.Elapsed */ };
timer.StateChangedEvent += (s, e) => { /* Started, Executing, Waiting, Stopped */ };

timer.Stop();

Enumerable Extensions

var item = items.TakeRandom();                     // random element
var shuffled = items.RandomOrder();                 // random order
var tail = items.TakeAllButFirst();                 // skip first
var init = items.TakeAllButLast();                  // skip last
var chunks = items.TakeChunks(10);                  // split into groups of 10
bool empty = EnumerableExtensions.IsNullOrEmpty(items);
var safe = EnumerableExtensions.EmptyIfNull(items); // never null

Luhn Check Digits

var check = "7992739871".CheckDigit();       // "3"
var full = "7992739871".AppendCheckDigit();   // "79927398713"
bool valid = "79927398713".HasValidCheckDigit(); // true

// Also works with int, long, and IList<int>
int digit = 36155.CheckDigit(); // 0

Password Hasher

PBKDF2-based password hashing.

var hash = PasswordHasher.HashPassword("myPassword");
bool ok = PasswordHasher.VerifyPassword("myPassword", hash);   // true
bool bad = PasswordHasher.VerifyPassword("wrong", hash);       // false

Collections

// Two-level concurrent dictionary
var dict = new ConcurrentTwoLevelDictionary<string, string, int>();
var (before, after) = dict.AddOrUpdate("users", "alice", 42);

// Observable concurrent dictionary (INotifyCollectionChanged)
var observable = new ObservableConcurrentDictionary<string, int>();
observable.CollectionChanged += (s, e) => { /* react to changes */ };
observable.Add("key", 1);

Semaphore Executor

Key-based async semaphore: same key = sequential, different keys = concurrent.

var executor = new SemaphoreExecutor<string>();
var result = await executor.ExecuteAsync("user-123", async () =>
{
    // Only one operation per key at a time
    return await ProcessAsync();
});

Claims Extensions

Extract identity, email, and display name from ClaimsPrincipal, ClaimsIdentity, or raw claims.

// Identity extraction (checks sub, oid, nameid, uid, NameIdentifier)
var (identity, type) = principal.GetIdentity();

// Email extraction (checks email, emails, preferred_username, name)
var email = principal.GetEmail();
var domain = principal.GetEmailDomain();

// Display name extraction — tries in order:
// 1. "name" (OIDC)  2. ClaimTypes.Name (WS-Fed)  3. "nickname" (Auth0)
// 4. given_name + family_name  5. ClaimTypes.GivenName + Surname
// 6. preferred_username (non-email)  7. email prefix, title-cased
var displayName = principal.GetDisplayName();
// e.g. "daniel.bohlin@example.com" → "Daniel Bohlin"

// Domain-based role assignment
principal.AddRoleForDomain("Developer", "example.com", "contoso.com");

Other Extensions

// Org number parsing (Swedish format)
if ("556123-4567".TryParseOrgNo(out var orgNo))
    Console.WriteLine(orgNo); // "556123-4567"

// Byte size formatting
long bytes = 1536;
Console.WriteLine(bytes.ToReadableByteSize(decimalPlaces: 1)); // "1.5 KB"

// Enum mapping
var target = sourceEnum.MapEnum<TargetEnum, SourceEnum>();

// Exception data helpers
throw new Exception("error")
    .AddData("userId", 123)
    .AddData("action", "save");

// Smart enum
public class Color : Enumeration
{
    public static readonly Color Red = new(1, "Red");
    public static readonly Color Blue = new(2, "Blue");
    private Color(int id, string name) : base(id, name) { }
}
var all = Enumeration.GetAll<Color>();

GitHub repo

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.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.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 (1)

Showing the top 1 NuGet packages that depend on Tharga.Toolkit.Standard:

Package Downloads
Tharga.Toolkit

Tools for hashing, claims, API keys, collections, DateTime, Luhn, Compare and more. Includes all features from Tharga.Toolkit.Standard.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.15.20 273 4/5/2026
1.15.19 454 3/25/2026
1.15.18 898 1/31/2026
1.15.16 348 12/28/2025
1.15.14 215 12/20/2025
1.15.12 828 12/1/2025
1.15.10 618 12/1/2025
1.15.8 529 11/30/2025
1.15.6 256 11/25/2025
1.15.4 232 11/25/2025
1.15.2 992 11/11/2025
1.14.3 228 11/1/2025
1.13.26 203 11/1/2025
1.13.24 265 10/31/2025
1.13.22 383 10/27/2025
1.13.20 416 10/14/2025
1.13.18 235 10/13/2025
1.13.16 366 8/16/2025
1.13.14 419 8/5/2025
1.13.12 725 7/22/2025
Loading failed