Plugin.BaseTypeExtensions

Welcome to Plugin.BaseTypeExtensions, a comprehensive .NET 9 library that provides powerful extension methods for base types, designed to enhance your development experience with clean, intuitive, and performance-optimized code.

🚀 Overview

This library extends .NET's fundamental types with a rich collection of utility methods, following modern .NET patterns and leveraging advanced generic constraints like INumber<T> and IComparable<T> for type safety and performance.

📅 Date & Time Extensions

  • DateTimeExtensions - DateTime manipulation and formatting utilities | DateTimeExtensions
  • DateOnlyExtensions - DateOnly type extensions for modern date handling | DateOnlyExtensions
  • TimeOnlyExtensions - TimeOnly type extensions for time-specific operations | TimeOnlyExtensions

🗂️ Collection Specific Extensions

🔍 Reflection & Metadata

🎲 Utility Extensions

🚀 Advanced Utilities

🛠️ Utility Classes

  • ComparableTools - Utility methods for comparable operations | ComparableTools
  • NumericRangeTools - Numeric range generation and manipulation | NumericRangeTools

🏁 Quick Start

Installation

dotnet add package Plugin.BaseTypeExtensions

Basic Usage

using Plugin.BaseTypeExtensions;

// String operations
string email = "  user@EXAMPLE.com  ";
string clean = email.NullIfDud()?.Trim().ToLowerInvariant();
bool isValid = clean.IsValidEmail(); // true

// Numeric operations
int value = 150;
int clamped = value.Clamp(0, 100); // 100
double percentage = value.ValueToPercentage(0, 200); // 0.75

// Collection operations
var numbers = Enumerable.Range(1, 100);
var chunks = numbers.ChunkBy(10); // 10 chunks of 10 items each

// Async operations with timeout
var result = await SomeLongRunningTask()
    .WithTimeoutInMs(5000); // 5 second timeout

// TimeSpan formatting
var duration = TimeSpan.FromHours(2.5);
string readable = duration.ToReadableString(); // "2h 30m"
string human = duration.ToHumanReadable(); // "2 hours, 30 minutes"

// CancellationToken with timeout
using var cts = new CancellationTokenSource();
var timeoutToken = cts.Token.WithTimeout(TimeSpan.FromMinutes(5));
await ProcessDataAsync(timeoutToken);

🎯 Design Principles

Performance First

  • Aggressive Inlining: Critical methods use MethodImpl(MethodImplOptions.AggressiveInlining)
  • Zero Allocations: Minimal memory allocation in hot paths
  • Generic Constraints: Compile-time type safety with INumber<T>, IComparable<T>

Type Safety

  • Modern .NET: Leverages .NET 9 features and nullable reference types
  • Generic Constraints: Prevents runtime type errors
  • Null Safety: Comprehensive null handling throughout

Clean API Design

  • Intuitive Names: Methods named for clarity and discoverability
  • Consistent Patterns: Uniform naming and parameter conventions
  • Fluent Interface: Chain operations naturally

Comprehensive Testing

  • 95% Coverage: Minimum test coverage requirement (386+ tests)
  • Edge Cases: Extensive edge case and error condition testing
  • Thread Safety: Deterministic testing for async operations
  • Performance Tests: Benchmarks for critical paths

🚀 Advanced Examples

Data Processing Pipeline

var results = await data
    .EmptyIfNull()
    .WhereIf(includeInactive, item => item.IsActive)
    .DistinctBy(item => item.Id)
    .ProcessInBatchesAsync(100, async batch =>
    {
        return await ProcessBatchAsync(batch)
            .WithTimeoutInMs(30000);
    });

Configuration Validation

public bool ValidateConfig(Dictionary<string, string> config)
{
    return config
        .EmptyIfNull()
        .Where(kvp => kvp.Key.IsValidEmail() || kvp.Key.IsValidUrl())
        .All(kvp => kvp.Value.NullIfDud() != null);
}

Time-Based Operations

public async Task<T> ExecuteWithProgressAsync<T>(
    Func<CancellationToken, Task<T>> operation,
    TimeSpan estimatedDuration,
    IProgress<string> progress)
{
    var timeoutToken = CancellationToken.None.WithTimeout(estimatedDuration.Multiply(1.5));
    var stopwatch = Stopwatch.StartNew();

    var progressTask = Task.Run(async () =>
    {
        while (!timeoutToken.IsCancellationRequested)
        {
            var elapsed = stopwatch.Elapsed;
            var remaining = estimatedDuration - elapsed;
            progress?.Report($"Elapsed: {elapsed.ToReadableString()}, Est. Remaining: {remaining.ToReadableString()}");

            await Task.Delay(TimeSpan.FromSeconds(1), timeoutToken);
        }
    });

    try
    {
        return await operation(timeoutToken);
    }
    finally
    {
        stopwatch.Stop();
    }
}

Async Coordination with Multiple Timeouts

public async Task<ProcessingResult> CoordinatedProcessingAsync(
    IEnumerable<ProcessingTask> tasks,
    ProcessingOptions options)
{
    using var masterCts = new CancellationTokenSource();
    var overallTimeout = masterCts.Token.WithTimeout(options.OverallTimeout);

    var results = new ConcurrentBag<TaskResult>();

    await tasks
        .EmptyIfNull()
        .ProcessInBatchesAsync(options.MaxConcurrency, async batch =>
        {
            var batchTasks = batch.Select(async task =>
            {
                var taskTimeout = overallTimeout.WithTimeout(options.TaskTimeout);

                try
                {
                    var result = await task.ExecuteAsync(taskTimeout);
                    results.Add(new TaskResult { Success = true, Result = result });
                }
                catch (OperationCanceledException) when (overallTimeout.IsCancellationRequested)
                {
                    results.Add(new TaskResult { Success = false, Error = "Overall timeout" });
                }
                catch (Exception ex)
                {
                    results.Add(new TaskResult { Success = false, Error = ex.Message });
                }
            });

            await Task.WhenAll(batchTasks);
        });

    return new ProcessingResult
    {
        Results = results.ToList(),
        Duration = masterCts.Token.AsTask().IsCompleted ? options.OverallTimeout : TimeSpan.Zero
    };
}

🏗️ Architecture

The library follows a modular extension pattern:

  • Extension Classes: One class per base type (StringExtensions, NumericExtensions, etc.)
  • Utility Classes: Helper classes for complex operations (ComparableTools, NumericRangeTools)
  • Performance Optimization: Strategic use of aggressive inlining and generic constraints
  • Comprehensive Testing: 386+ tests covering all functionality with edge cases
  • Thread-Safe Patterns: Robust async and threading support

⚡️ Performance

All extension methods are optimized for performance:

  • Minimal Allocations: String operations reuse buffers where possible
  • Efficient Algorithms: Optimized for common use cases
  • Inlined Operations: Hot paths use aggressive inlining
  • Culture-Independent: Reliable behavior across different system cultures
  • Benchmarked: Regular performance testing ensures optimization

🤝 Contributing

We welcome contributions! The codebase follows strict quality standards:

  • Zero Warnings: TreatWarningsAsErrors=true enforced
  • 95% Test Coverage: Comprehensive test coverage required
  • Modern .NET: Full use of .NET 9 features and patterns
  • Performance Focus: All additions must maintain performance standards

📄 License

This project is licensed under the MIT License.