Lightweight extension methods for booleans, strings and collections. Includes: - Bool: AddNot (conditionally prefixes a string), ToYesOrNo (converts bool to "yes"/"no"). - Collection: IsEmpty, IsNotEmpty, and Chunk (splits a collection into sublists with a maximum length). - Memoize: simple caching for Func delegates (supports up to two arguments). Targeted for .NET 8.0, .NET 9.0, and .NET 10.0. MIT licensed.
$ dotnet add package Eventualist.ExtensionsA collection of lightweight, focused extension methods for common .NET types. Originally created for personal projects but available for anyone to use.
This library is primarily developed for personal use and experimentation. While it is publicly available and contributions are welcome, please note:
For production use, please thoroughly test the library in your specific context and consider pinning to a specific version.
AddNot(string text, string negation = "not ") - Conditionally prefixes a string with a negation word based on the boolean value
false.AddNot("implemented") // Returns "not implemented"
true.AddNot("implemented") // Returns "implemented"
ToYesOrNo(string yes = "yes", string no = "no", string unknown = "unknown") - Converts boolean to yes/no strings (supports nullable booleans)
true.ToYesOrNo() // Returns "yes"
false.ToYesOrNo() // Returns "no"
((bool?)null).ToYesOrNo() // Returns "unknown"
IsEmpty<T>() - Returns true if the collection is emptyIsNotEmpty<T>() - Returns true if the collection contains any elementsWhereNotNull<T>() - Filters out null values from a collection
var items = new[] { "a", null, "b", null, "c" };
var filtered = items.WhereNotNull(); // Returns ["a", "b", "c"]
ContainsAll<T>(params T[] items) - Determines whether the collection contains all of the specified items
var numbers = new[] { 1, 2, 3, 4, 5 };
numbers.ContainsAll(2, 4) // Returns true
numbers.ContainsAll(2, 6) // Returns false
Divide<T>(int maxLength) - Splits a collection into sublists with a specified maximum length
var numbers = new[] { 1, 2, 3, 4, 5, 6, 7 };
var chunks = numbers.Divide(3); // Returns [[1,2,3], [4,5,6], [7]]
IsEven() / IsOdd() - Determines whether a number is even or odd
4.IsEven() // Returns true
5.IsOdd() // Returns true
IsBetween(min, max) - Determines whether a number is between two values (inclusive)
5.IsBetween(1, 10) // Returns true
15.IsBetween(1, 10) // Returns false
Clamp(min, max) - Clamps a number to be within a specified range
15.Clamp(0, 10) // Returns 10
(-5).Clamp(0, 10) // Returns 0
5.Clamp(0, 10) // Returns 5
ToOrdinal() - Converts a number to its ordinal string representation
1.ToOrdinal() // Returns "1st"
2.ToOrdinal() // Returns "2nd"
3.ToOrdinal() // Returns "3rd"
21.ToOrdinal() // Returns "21st"
Titleize() - Converts a string to title case
"helloWorld".Titleize() // Returns "Hello World"
"SHOUTING_TEXT".Titleize() // Returns "Shouting_TEXT"
"snake_case_example".Titleize() // Returns "Snake_case_example"
ToSlug() - Converts a string to a URL-friendly slug
"Hello World!".ToSlug() // Returns "hello-world"
"C# Programming 101".ToSlug() // Returns "c-programming-101"
RemoveWhitespace() - Removes all whitespace characters from a string
"Hello World".RemoveWhitespace() // Returns "HelloWorld"
" spaces everywhere ".RemoveWhitespace() // Returns "spaceseverywhere"
Abbreviate(int maxLength, string abbreviationSymbol = "...") - Shortens a string to a maximum length with ellipsis
"This is a very long sentence that needs to be abbreviated".Abbreviate(30)
// Returns "This is a very long..."
"Short text".Abbreviate(20) // Returns "Short text" (no truncation needed)
Truncate(int maxLength, string suffix = "...") - Truncates a string to a maximum length
"Hello World".Truncate(8) // Returns "Hello..."
"Hello World".Truncate(8, "[more]") // Returns "Hel[more]"
StripHtml() - Removes HTML tags from a string
"<p>Hello <strong>World</strong></p>".StripHtml() // Returns "Hello World"
HasCorrectExtension(params string[] additionalExtensions) - Validates file extension (defaults to image extensions)
"photo.jpg".HasCorrectExtension() // Returns true
"document.pdf".HasCorrectExtension() // Returns false
"document.pdf".HasCorrectExtension("pdf", "doc") // Returns true
ConvertToMimeType() - Converts file extension to MIME type
"jpg".ConvertToMimeType() // Returns "image/jpeg"
".png".ConvertToMimeType() // Returns "image/png"
"pdf".ConvertToMimeType() // Returns "application/pdf"
"unknown".ConvertToMimeType() // Returns "application/octet-stream"
ShowIfNone(string alternativeText = "None") - Returns alternative text if string is null or empty
string? emptyString = null;
emptyString.ShowIfNone() // Returns "None"
emptyString.ShowIfNone("N/A") // Returns "N/A"
"Hello".ShowIfNone() // Returns "Hello"
ToSentenceCase() - Converts camelCase or PascalCase to sentence case
"thisIsATest".ToSentenceCase() // Returns "this is a test"
"PascalCaseExample".ToSentenceCase() // Returns "pascal case example"
SplitCamelCase() - Splits camel case text into separate words
"camelCaseString".SplitCamelCase() // Returns "camel Case String"
"HTTPSConnection".SplitCamelCase() // Returns "HTTPS Connection"
Capitalize(CultureInfo? culture = null) - Capitalizes the first character
"hello".Capitalize() // Returns "Hello"
Left(int length) / Right(int length) - Gets leftmost or rightmost characters
"Hello World".Left(5) // Returns "Hello"
"Hello World".Right(5) // Returns "World"
Reverse() - Reverses a string
"Hello".Reverse() // Returns "olleH"
ParseDateFromDateTimePicker(CultureInfo? culture = null) - Parses yyyy/MM/dd format dates
"2024/12/25".ParseDateFromDateTimePicker() // Returns DateTime(2024, 12, 25)
"invalid".ParseDateFromDateTimePicker() // Returns null
Age() / Age(DateTime asOfDate) - Calculates the age in years from this date
var birthdate = new DateTime(1990, 1, 1);
var age = birthdate.Age(); // Returns the current age
ToRelativeTime() / ToRelativeTime(DateTime referenceDate) - Returns a human-readable relative time string
var pastDate = DateTime.Now.AddHours(-2);
pastDate.ToRelativeTime() // Returns "2 hours ago"
IsToday() / IsTomorrow() / IsYesterday() - Determines whether the date is today, tomorrow, or yesterday
DateTime.Today.IsToday() // Returns true
DateTime.Today.AddDays(1).IsTomorrow() // Returns true
StartOfWeek(DayOfWeek startOfWeek = DayOfWeek.Sunday) / EndOfWeek(DayOfWeek startOfWeek = DayOfWeek.Sunday) - Returns the start or end of the week
var date = new DateTime(2024, 1, 10); // Wednesday
date.StartOfWeek(DayOfWeek.Monday) // Returns Monday of that week
date.EndOfWeek(DayOfWeek.Monday) // Returns Sunday at 23:59:59.999
MustComeBefore Attribute - Validation attribute to ensure one DateTime property precedes another
public class TimePeriod
{
[MustComeBefore("EndDate")]
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
Memoize() - Automatically caches function results for improved performance
Func<int, int> expensiveOperation = x => { /* ... */ };
var memoized = expensiveOperation.Memoize();
// First call computes and caches result
var result1 = memoized(5);
// Subsequent calls with same input return cached result
var result2 = memoized(5); // Instant return from cache
IsNull<T>() / IsNotNull<T>() - Determines whether an object is null or not null
string? text = null;
text.IsNull<string>() // Returns true
"hello".IsNotNull<string>() // Returns true
⚠️ Note (v5.0.2.0+): Due to the migration to C# 14 extension syntax, the type parameter may need to be explicitly specified in some contexts where type inference is ambiguous.
ThrowIfNull<T>() - Throws an ArgumentNullException if the object is null
string? text = GetText();
text.ThrowIfNull<string>(); // Throws if text is null, otherwise returns text
var upper = text.ThrowIfNull<string>().ToUpper(); // Method chaining
⚠️ Note (v5.0.2.0+): The type parameter may need to be explicitly specified in some contexts. The method also supports an overload with a custom error message:
text.ThrowIfNull<string>("Text cannot be null");
Add the package reference to your project:
<PackageReference Include="Eventualist.Extensions" Version="5.0.0.1" />
Or via the .NET CLI:
dotnet add package Eventualist.Extensions
The NuGet package includes separate assemblies for each supported framework, allowing you to use it in projects targeting .NET 8, .NET 9, or .NET 10.
IsNull<T>(), IsNotNull<T>(), ThrowIfNull<T>()) may require explicit type parameters in some contexts due to C# 14 extension syntax migration. For example, use text.IsNull<string>() instead of text.IsNull() when the compiler cannot infer the type.CallerArgumentExpression in ThrowIfNull to correctly capture parameter namesextension syntax for improved readabilityMustComeBefore attributes on a single propertyThe project includes a GitHub Actions CI workflow (.github/workflows/dotnet.yml) that automatically:
# Restore dependencies
dotnet restore
# Build the solution
dotnet build
# Run tests
dotnet test
# Create NuGet package
dotnet pack
Contributions are welcome! Please see our Contributing Guidelines for detailed information on how to contribute.
Quick start:
dotnet test locally to ensure all tests passFor bug reports, feature requests, or questions, please open an issue on GitHub.
For information about reporting security vulnerabilities and our security policies, please see our Security Policy.
Important: Do not report security vulnerabilities through public GitHub issues. Please email info@esoxsolutions.nl instead.
Iede Snoek
Email: info@esoxsolutions.nl
Company: Esox Solutions
This project is licensed under the MIT License - see the LICENSE file for details.
Copyright (c) 2022-2025 Esox Solutions