View on GitHub
Clean Architecture Toolkit for .NET
Clean Architecture Toolkit is a clean, modular architecture foundation for .NET applications.
It embraces SOLID principles and focuses on maintainability, testability, and scalability through clear boundaries and pluggable design.

|   HOME   |   MODULE OVERVIEW   |   DEMO   |

Cat.Extensions

Cat.Extensions is a collection of small, focused extension method libraries. Each namespace targets one type, IEnumerable, Expression, IServiceCollection, string, or general objects, and has no dependency on any other CAT module, so you can pull in just the ones you need.

Table of contents

NuGet packages

NuGet Version

Package Role Install Downloads
Cat.Enumerable.Extensions Extensions dotnet add package gmarginet.Cat.Enumerable.Extensions NuGet Downloads
Cat.Expression.Extensions Extensions dotnet add package gmarginet.Cat.Expression.Extensions NuGet Downloads
Cat.Object.Extensions Extensions dotnet add package gmarginet.Cat.Object.Extensions NuGet Downloads
Cat.ServiceCollection.Extensions Extensions dotnet add package gmarginet.Cat.ServiceCollection.Extensions NuGet Downloads
Cat.String.Extensions Extensions dotnet add package gmarginet.Cat.String.Extensions NuGet Downloads

Namespaces

Namespace What it holds
Cat.Enumerable.Extensions Extension methods for IEnumerable.
Cat.Expression.Extensions Extension methods for expressions.
Cat.Object.Extensions Extension methods for any object.
Cat.ServiceCollection.Extensions Extension methods for IServiceCollection.
Cat.String.Extensions Extension methods for strings.

Dependencies

Cat.Extensions has no dependencies on other CAT modules.

Usage

Register the module

This module has nothing to register. Add a using for the namespace you need, and the extension methods show up on the type.

Enumerable

IsEmpty checks whether a sequence has any elements. It works on any IEnumerable, and it reads at most one element.

using Cat.Enumerable.Extensions;

if (items.IsEmpty()) return;

Expression

GetMemberInfo resolves the member a lambda expression points to. It returns null when the expression isn’t a direct member access on its parameter.

using Cat.Expression.Extensions;

Expression<Func<Order, int>> expression = order => order.Id;
MemberInfo? member = expression.GetMemberInfo();            // the Id property

Expression<Func<Order, string>> nested = order => order.Customer.Name;
MemberInfo? nestedMember = nested.GetMemberInfo();          // the Name property

Expression<Func<Order, int>> calculation = order => order.Id + 1;
MemberInfo? none = calculation.GetMemberInfo();             // null

Object

Validate arguments without hand-written null and range checks. Every method returns the value when it passes, so you can use it inline.

Method The value must be Throws when
ThrowIfNull Not null The value is null.
ThrowIfLessThan At least the minimum The value is less than the minimum.
ThrowIfLessThanOrEqualTo Higher than the limit The value is less than or equal to the limit.
ThrowIfGreaterThan At most the maximum The value is greater than the maximum.
ThrowIfGreaterThanOrEqualTo Less than the limit The value is greater than or equal to the limit.

The four comparison methods use the default comparison of the type. Each also has an overload that takes your own IComparer<T>, placed right after the limit.

using Cat.Object.Extensions;

public Order(Customer customer, int quantity, int minQuantity, int maxQuantity)
{
    _customer = customer.ThrowIfNull(nameof(customer));
    _quantity = quantity.ThrowIfLessThan(1, nameof(quantity));
    minQuantity.ThrowIfGreaterThanOrEqualTo(maxQuantity, nameof(minQuantity), nameof(maxQuantity));
}

ThrowIfLessThan, ThrowIfLessThanOrEqualTo and ThrowIfGreaterThan take the name of the parameter to report. ThrowIfGreaterThanOrEqualTo compares two values, so it takes the name of both.

With your own comparer:

_start = start.ThrowIfGreaterThanOrEqualTo(end, StringComparer.OrdinalIgnoreCase, nameof(start), nameof(end));

ServiceCollection

HasRegistrationFor checks whether a service is already registered. Use it to register something once, however many modules ask for it.

using Cat.ServiceCollection.Extensions;

if (services.HasRegistrationFor<ICatEventAggregator>()) return;

Pass a type when you only have it at runtime. An open generic type matches any registration for that generic definition:

var hasHandlers = services.HasRegistrationFor(typeof(ICatCommandHandler<>));

String

NamedFormat formats a named message template. The names are only for readability. Values are matched by position, in order.

using Cat.String.Extensions;

var message = "User {UserId} logged in at {Timestamp}".NamedFormat(userId, timestamp);
var padded = "Total: {Total,10:N2}".NamedFormat(total);

CreateRegex compiles a regular expression with a two-second matching timeout, so a pathological pattern can’t hang the caller.

var regex = @"^\d{3}-\d{4}$".CreateRegex();
var ignoreCase = @"^[a-z]+$".CreateRegex(RegexOptions.IgnoreCase);

Errors and exceptions

These methods throw exceptions. They don’t return responses.

Method Situation What you get
ThrowIfNull The argument is null An ArgumentNullException with the parameter name you passed.
ThrowIfLessThan The value is less than the minimum An ArgumentOutOfRangeException for the parameter you passed, with the actual value and the message <paramName> must be <minimum> or higher.
ThrowIfLessThanOrEqualTo The value is less than or equal to the limit An ArgumentOutOfRangeException for the parameter you passed, with the actual value and the message <paramName> must be higher than <limit>.
ThrowIfGreaterThan The value is greater than the maximum An ArgumentOutOfRangeException for the parameter you passed, with the actual value and the message <paramName> must be <maximum> or lower.
ThrowIfGreaterThanOrEqualTo The value is greater than or equal to the limit An ArgumentOutOfRangeException for the value name you passed, with the actual value and the message <valueName> must be less than <limitName>.
The four comparison methods without a comparer T doesn’t implement IComparable<T> An ArgumentException with the message T is not IComparable<T>.
CreateRegex The pattern isn’t a valid regular expression An ArgumentException with the message Invalid regex expression: <pattern>. The original exception is the inner exception.
NamedFormat There are fewer values than placeholders A FormatException from string.Format.
GetMemberInfo The expression isn’t a direct member access null. No exception.
IsEmpty, HasRegistrationFor Never These don’t throw for valid input.