KExtensions 0.1.2

dotnet add package KExtensions --version 0.1.2
                    
NuGet\Install-Package KExtensions -Version 0.1.2
                    
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="KExtensions" Version="0.1.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="KExtensions" Version="0.1.2" />
                    
Directory.Packages.props
<PackageReference Include="KExtensions" />
                    
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 KExtensions --version 0.1.2
                    
#r "nuget: KExtensions, 0.1.2"
                    
#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 KExtensions@0.1.2
                    
#: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=KExtensions&version=0.1.2
                    
Install as a Cake Addin
#tool nuget:?package=KExtensions&version=0.1.2
                    
Install as a Cake Tool

KExtensions

A small, pragmatic collection of high‑quality C# extension methods and tiny utilities for everyday .NET apps and libraries. Focused on ergonomics, readability, and reducing boilerplate.

License: MIT Target Frameworks NuGet Downloads

Table of contents

Install

Using the .NET CLI:

dotnet add package KExtensions

Using Package Manager Console:

Install-Package KExtensions

Quick start

using KExtensions;

// Strings
var title = "hello world".ToTitleCase();        // "Hello World"
var kebab = "HelloWorldXML".ToKebabCase();      // "hello-world-xml"
var part  = "id=42&lang=en".SubstringAfter("id="); // "42&lang=en"

// Collections & arrays
var last3 = new[] { 1, 2, 3, 4, 5 }.TakeLast(3); // Span<int> { 3,4,5 }
var first2 = new[] { 1, 2, 3 }.Take(2);          // int[] { 1, 2 }

// Numbers
int flag = true.ToInt();       // 1
bool b = 1.ToBoolean();        // true
var inc = 10.Increment();      // 11
var pct = 200.CalculatePercentage(10); // 20 (10%)

// Date & time (parsing helpers)
var d1 = "05/11/2025".ParseDate();                    // dd/MM/yyyy by default
var t1 = "13:30:00".ParseTime();                      // HH:mm:ss by default
var dt = "25/12/2025 23:59:59".ParseDateTime();

// Tasks
Task.Run(async () => { /* ... */ }).Forget();         // fire-and-forget safely

// Console table (NET 10+)
#if NET10_0_OR_GREATER
Console.Table(new[] { new { Id = 1, Name = "A" }, new { Id = 2, Name = "B" } });
#endif

What you get

KExtensions groups extensions by domain so you can discover functionality quickly. Below are the most commonly used areas with practical examples.

Ranges

use .. to create ranges:

foreach (var i in 1..10) { /* ... */ }

Strings

Rich helpers for empty/blank checks, casing, slicing, partitions, simple templating, and byte conversions. Highlights:

  • Emptiness & blankness

    "".IsEmpty();                 // true
    ((string?)null).IsNullOrEmpty();   // true
    "  ".IsBlank();               // true (only whitespace)
    "text".IsNotNullOrBlank();    // true
    "foo".Or("alt");             // "foo"
    ((string?)null).OrEmpty();     // ""
    
  • Slicing & spans

    "abcdef".Take(2);            // "ab"
    "abcdef".Drop(2);            // "cdef"
    "abcdef".DropLast(2);        // "abcd"
    "abcdef".TakeLast(3);        // "def"
    var span = "abcdef".DropSpan(2);     // ReadOnlySpan<char> "cdef"
    
  • Classification & cleanup

    "123abc".HasDigits();         // true
    "123abc".HasLetters();        // true
    "a_b-cD".ToKebabCase();       // "a-b-cd"
    "aB".ToCamelCase();           // "aB" (safe camelization)
    "abc!@#".RemoveNonAlphanumeric(); // "abc"
    
  • Similarity & counts

    "kitten".StringMatchPercentage("sitting"); // 0..100 double
    "one two  three".WordCount();               // 3
    
  • Parts before/after

    "path/to/file.txt".SubstringBefore("/");      // "path"
    "path/to/file.txt".SubstringAfterLast("/");  // "file.txt"
    
  • Lightweight placeholders/interpolation

    var s = "Hello, {name}!".Interpolate(new { name = "Safwan" });
    // => "Hello, Safwan!"
    
    var f = "Speed is %speed%".FormatArgs(new { speed = 42 });
    // => "Speed is 42"
    
  • Bytes & base64

    var bytes = "hello".ToByteArray();
    var raw   = "aGVsbG8=".Base64StringToByteArray();
    

Collections & arrays

Ergonomic taking, dropping, batching, reversing, and simple for‑each helpers. Works with T[], Span<T>, IEnumerable<T>.

var first = new[] {1,2,3,4}.Take(2);           // int[] {1,2}
var last  = new[] {1,2,3,4}.TakeLast(2);       // Span<int> {3,4}
var rest  = new[] {1,2,3,4}.Drop(2);           // Span<int> {3,4}
var skip2 = Enumerable.Range(1,5).Drop(2);     // IEnumerable<int> {3,4,5}
var rev   = new[] {1,2,3}.Reverse();           // IEnumerable<int> {3,2,1}
var batches = Enumerable.Range(1,5).Batch(2);  // [[1,2],[3,4],[5]]

new[] {"a","b"}.ForEach(x => Console.WriteLine(x));
var dt = new[] { new { Id=1, Name="A" } }.ToDataTable();

JoinExtensions.JoinToString builds readable joined strings with prefix/postfix/limit:

var s = new[]{1,2,3}.JoinToString(
    separator: ", ", prefix: "[", postfix: "]", limit: -1,
    transform: x => (x * 10).ToString());
// => "[10, 20, 30]"

Numbers & percentages

Generic number helpers using INumber<T> plus practical conversions:

1.ToBoolean();        // true
false.ToInt();        // 0
5.Increment();        // 6
10.Decrement(3);      // 7
(-4).ToPositive();    // 4
4.ToNegative();       // -4
200.CalculatePercentage(10); // 20 (10%)
200.CalculatePercentage(0.25m); // 50 (25%)

Parsing numbers from strings

Convenience methods that consistently use invariant culture:

"$1,234.56".ParseCurrency();        // 1234.56m (strict)
"USD 1,234.56".ParseCurrencyLoose(); // 1234.56m (tolerant)
"3,14".ParseDecimalInvariant();     // 3.14m
"42".ToInt();                       // 42
"3.5".ToDouble();                   // 3.5

Date & time (string to DateTime)

Simple, explicit parsing helpers with formats:

"05/11/2025".ParseDate();                  // dd/MM/yyyy
"13:45:00".ParseTime();                    // HH:mm:ss
"25/12/2025 23:59:59".ParseDateTime();     // dd/MM/yyyy HH:mm:ss
"2025-10-25".ParseExactInvariant("yyyy-MM-dd");

Spans

Efficient concatenation helpers via SpanUtils:

ReadOnlySpan<byte> a = stackalloc byte[] {1,2};
ReadOnlySpan<byte> b = stackalloc byte[] {3,4};
var joined = a.Concat(b); // byte[] {1,2,3,4}

Tasks

Fire‑and‑forget helpers and task result/exception split blocks:

someTask.Forget();
await someTask.SafeFireAndForget();

var (resultBlock, exceptionBlock) = someTaskReturningT.SplitIntoBlocks();

Paths & files

Utilities for JSON‑safe path literals and validation:

Environment.SpecialFolder.MyDocuments.ItPath();
"C:/temp/file.txt".SerializePathForJson();

if (!"report?.txt".IsValidAsFileName(out var err))
{
    Console.WriteLine(err.GetMessage()); 
}

"C:/windows".PathExists();

Types/DirectoryPath is a small span‑friendly struct for directory operations:

using KExtensions.Types;

DirectoryPath root = new DirectoryPath("C:/data");
var sub = root.Combine("logs");
if (sub.Exists())
{
    foreach (var f in sub.GetFiles("*.log")) { /* ... */ }
}

Console output (NET 10+)

Pretty‑print tables to the console without bringing heavy dependencies:

#if NET10_0_OR_GREATER
Console.Table(new [] { new { Id = 1, Name = "Alpha" }, new { Id = 2, Name = "Beta" } });
// +----+-------+
// | Id | Name  |
// +----+-------+
// | 1  | Alpha |
// | 2  | Beta  |
// +----+-------+
#endif

Kotlin-style scope functions

Lightweight, Kotlin-inspired helpers from AnyExtensions to make object configuration and flow nicer:

var person = new Person()
    .Apply(p => { p.Name = "Safwan"; p.Age = 30; })
    .Also(p => Console.WriteLine($"Created: {p.Name}"));

int len = "hello".Run(s => s.Length); // 5

string? nonNull = "value".TakeIf(v => v.Length > 0);   // "value"
string? nullVal = "".TakeUnless(v => v.Length > 0);     // null
  • Run(obj => ...) returns the lambda result.
  • Let(x => ...) same semantics as Run but for stylistic preference.
  • Apply(x => { ... }) returns the original object (for configuration chaining).
  • Also(x => { side effects }) returns the original object.
  • TakeIf(predicate) / TakeUnless(predicate) return the object or null.

Boolean helpers

Small helpers to reduce if boilerplate and to filter reference types:

true.IfTrue(() => DoWork());
false.IfFalse(() => Log("was false"));

var kept = someRef.TakeIf(x => x != null); // returns T? based on predicate

Definitions live in BoolExtensions.

DataTable helpers

Utilities when you need to interop with DataTable/DataRow:

// Binary search on a sorted column
int idx = table.BinarySearch(columnName: "Id", value: 42);

// Clone a DataRow to a new detached row with the same schema
DataRow copy = row.Clone();

Notes:

  • BinarySearch assumes the target column is already sorted in ascending order by Comparer<object>.Default.
  • Clone copies the ItemArray and returns a new row from the same table schema.

Speed & Size formatting

Human-readable formatting for throughput and sizes:

123_456L.SpeedFormat();           // "120.56 KB/s"
123_456_789L.SpeedFormat(true);   // "117.74 MB/s"

10_485_760L.SizeFormat(inMB: true);  // "10.00 MB"
10_240L.SizeFormat(inMB: false);     // "10.00 KB"

See SpeedAndSizeExtensions.

DateTime extras

Additional helpers beyond parsing:

var dates = new DateTime(2025, 1, 1).GetDatesArrayTo(new DateTime(2025, 1, 05));
// => [2025-01-01, 2025-01-02, 2025-01-03, 2025-01-04]

var hijri = DateTime.Now.ToHijriDate(); // e.g., "1447/05/20"

Note: ToHijriDate uses System.Globalization.HijriCalendar.

Generic helpers

General utilities for conversion, numeric checks, quick dumps, and type checks from GenericExtensions:

object any = "42";
int? n = any.To<int>();                  // 42 (invariant culture)
decimal? d = "3,14".To<decimal>();      // 3.14 (invariant culture)

42.IsNumeric();                          // true
"x".IsNumeric();                         // false

var user = new { Id = 1, Name = "A" };
user.Dump();                              // writes to Console
user.Dump("Created");                    // "Created: { ... }"

"hello".TypeIs<string>();                // true
"hello".TypeIsExact<object>();           // false (exact type check)
"hello".TypeIsAssignableTo<object>();    // true

Notes:

  • To<T> is forgiving and returns default on conversion failure; it uses invariant culture for numbers and supports enums by name or underlying value.

Delegate helpers

Invoke multicast delegates without losing handlers when one throws, from DelegateExtensions:

// Multicast Action
Action chain = () => Console.WriteLine("A");
chain += () => throw new InvalidOperationException("boom");
chain += () => Console.WriteLine("C");

chain.SafeInvoke(); // Executes all; then throws AggregateException with 1 inner exception

// Any delegate with parameters
EventHandler? onChanged = null;
onChanged += (s, e) => Console.WriteLine("first");
onChanged += (s, e) => throw new Exception("bad");
onChanged.SafeInvoke(null, EventArgs.Empty); // calls both; then throws AggregateException

Dict-of-lists & array helpers

Convenience when working with Dictionary<TKey, ICollection<T>> and arrays, see DictListExtensions:

var dict = new Dictionary<string, List<int>>();

dict.AddToCollection("a", 1);
dict.AddToCollection("a", 2);
// dict["a"] == [1, 2]

bool removed = dict.RemoveFromCollection("a", 1); // true; removes key when last item removed

var l1 = new List<int> {1,2,3};
var l2 = new List<int> {3,2,1};
l1.CompareLists(l2, isOrdered: false); // true
l1.CompareLists(l2, isOrdered: true);  // false

new[]{1,2,3,4}.ContainsSequence(new[]{2,3}); // true

Char helpers

ASCII / Unicode character classification shortcuts from CharExtensions:

'a'.IsAsciiLetterLower(); // true
'Z'.IsAsciiLetterUpper(); // true
'5'.IsAsciiDigit();       // true

char.IsLetter('ß');       // baseline .NET
't'.IsLetter();           // wrapper over char APIs

'a'.ToUpperInvariant();   // 'A'
'a'.IsBetween('a','f');   // true

// On strings at index
"A1".IsDigitAt(1);       // true
"A1".IsLetterAt(0);      // true

Nullable and type helpers

Helpers for dealing with Nullable<T> and exact/nullable checks (from DictListExtensions), and type predicates (from GenericExtensions):

Type t = typeof(int);

t.IsNullable();                // false
t.ToNullable();                // typeof(int?)

Type? maybe = typeof(int?);
maybe.IsExactOrNullable<int>(); // true

object o = "str";
o.TypeIs<string>();             // true
o.TypeIsExact<object>();        // false

Supported frameworks

  • net8.0
  • net9.0
  • net10.0

The library uses modern C# features (LangVersion: preview). Some APIs are conditionally available on newer TFMs (for example: console Table<T> on .NET 10+).

Design goals

  • Small and focused surface area
  • Predictable behavior and naming
  • Invariant culture for numeric/date parsing helpers
  • Allocation‑aware implementations where it matters (spans, stackalloc, batching)

FAQ

  • Why extension methods? Because they compose well, keep call sites readable, and are easy to adopt incrementally.
  • Are there breaking changes? We follow SemVer during preview; breaking changes will bump the minor/major version appropriately.
  • Can I use parts of it only? Yes. It’s a single assembly; just call the extensions you need.

Roadmap (ideas)

  • More span‑based string utilities
  • Additional parsing helpers with Try‑ versions
  • Extra LINQ‑like utilities for async streams
  • Benchmarks and performance docs

Contributing

Issues and PRs are welcome. Please try to keep additions small and focused. If you’re unsure, open an issue to discuss the idea first.

License

MIT © Safwan Abdulghani

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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 is compatible.  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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.2 301 11/14/2025
0.1.1 221 10/29/2025
0.1.0 128 10/25/2025