KExtensions 0.1.2
dotnet add package KExtensions --version 0.1.2
NuGet\Install-Package KExtensions -Version 0.1.2
<PackageReference Include="KExtensions" Version="0.1.2" />
<PackageVersion Include="KExtensions" Version="0.1.2" />
<PackageReference Include="KExtensions" />
paket add KExtensions --version 0.1.2
#r "nuget: KExtensions, 0.1.2"
#:package KExtensions@0.1.2
#addin nuget:?package=KExtensions&version=0.1.2
#tool nuget:?package=KExtensions&version=0.1.2
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.
Table of contents
- Install
- Quick start
- What you get
- Ranges
- Strings
- Collections & arrays
- Numbers & percentages
- Parsing numbers from strings
- Date & time (string to DateTime)
- Spans
- Tasks
- Paths & files
- Console output (NET 10+)
- Kotlin-style scope functions
- Boolean helpers
- DataTable helpers
- Speed & Size formatting
- DateTime extras
- Generic helpers
- Delegate helpers
- Dict-of-lists & array helpers
- Char helpers
- Nullable and type helpers
- Supported frameworks
- Design goals
- FAQ
- Roadmap (ideas)
- Contributing
- License
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(); // 3Parts 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 asRunbut 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 ornull.
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:
BinarySearchassumes the target column is already sorted in ascending order byComparer<object>.Default.Clonecopies theItemArrayand 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 returnsdefaulton 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 | Versions 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. |
-
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.