ComparableExtensions
Provides enhanced comparison operations for IComparable
Overview
The ComparableExtensions class offers fluent comparison operations and range checking utilities for any type that implements IComparable<T>.
API Reference
Core Methods
Range Operations
IsBetween<T>(T value, T min, T max, bool inclusive = true) where T : IComparable
Checks if a value is within a specified range.
int value = 50;
bool inRange = value.IsBetween(0, 100); // true (inclusive by default)
bool inRangeExclusive = value.IsBetween(0, 100, false); // true
DateTime date = DateTime.Now;
bool isToday = date.IsBetween(DateTime.Today, DateTime.Today.AddDays(1), false);
IsOutsideRange<T>(T value, T min, T max, bool inclusive = true) where T : IComparable
Checks if a value is outside a specified range.
int value = 150;
bool outside = value.IsOutsideRange(0, 100); // true
double temperature = -10.5;
bool belowFreezing = temperature.IsOutsideRange(0.0, 100.0); // true
Fluent Comparisons
IsGreaterThan<T>(T value, T other) where T : IComparable
Checks if value is greater than another value.
int score = 85;
bool passedTest = score.IsGreaterThan(70); // true
DateTime deadline = DateTime.Now.AddDays(5);
bool hasTime = deadline.IsGreaterThan(DateTime.Now); // true
IsLessThan<T>(T value, T other) where T : IComparable
Checks if value is less than another value.
double price = 19.99;
bool affordable = price.IsLessThan(25.00); // true
IsGreaterThanOrEqual<T>(T value, T other) where T : IComparable
Checks if value is greater than or equal to another value.
int age = 18;
bool canVote = age.IsGreaterThanOrEqual(18); // true
IsLessThanOrEqual<T>(T value, T other) where T : IComparable
Checks if value is less than or equal to another value.
int inventory = 5;
bool lowStock = inventory.IsLessThanOrEqual(10); // true
Boundary Operations
Min<T>(T value, T other) where T : IComparable
Returns the smaller of two values.
int result = 10.Min(5); // 5
DateTime earlier = DateTime.Now.Min(DateTime.Today); // DateTime.Today
Max<T>(T value, T other) where T : IComparable
Returns the larger of two values.
int result = 10.Max(5); // 10
double higher = 3.14.Max(2.71); // 3.14
Advanced Operations
Conditional Comparisons
CompareWith<T>(T value, T other) where T : IComparable
Returns the comparison result as an integer.
int comparison = 5.CompareWith(3); // 1 (greater)
int comparison2 = 3.CompareWith(5); // -1 (less)
int comparison3 = 5.CompareWith(5); // 0 (equal)
Range Validation
EnsureInRange<T>(T value, T min, T max, string parameterName = null) where T : IComparable
Throws exception if value is outside the specified range.
public void SetVolume(int volume)
{
volume.EnsureInRange(0, 100, nameof(volume));
_volume = volume;
}
public void ScheduleMeeting(DateTime meetingTime)
{
meetingTime.EnsureInRange(DateTime.Today, DateTime.Today.AddYears(1), nameof(meetingTime));
_meetings.Add(meetingTime);
}
Practical Examples
Validation Framework
public class ValidationHelper
{
public static ValidationResult ValidateAge(int age)
{
if (age.IsBetween(0, 150))
return ValidationResult.Success;
return new ValidationResult("Age must be between 0 and 150");
}
public static ValidationResult ValidateScore(double score)
{
if (score.IsBetween(0.0, 100.0))
return ValidationResult.Success;
return new ValidationResult("Score must be between 0 and 100");
}
public static ValidationResult ValidateDate(DateTime date, DateTime minDate, DateTime maxDate)
{
if (date.IsBetween(minDate, maxDate))
return ValidationResult.Success;
return new ValidationResult($"Date must be between {minDate:yyyy-MM-dd} and {maxDate:yyyy-MM-dd}");
}
}
Range-Based Logic
public class PricingEngine
{
public decimal CalculateDiscount(decimal orderAmount)
{
return orderAmount switch
{
var amount when amount.IsLessThan(50m) => 0m,
var amount when amount.IsBetween(50m, 100m) => 0.05m,
var amount when amount.IsBetween(100m, 500m) => 0.10m,
var amount when amount.IsGreaterThan(500m) => 0.15m,
_ => 0m
};
}
public string GetCustomerTier(int totalOrders)
{
return totalOrders switch
{
var orders when orders.IsLessThan(5) => "Bronze",
var orders when orders.IsBetween(5, 25) => "Silver",
var orders when orders.IsBetween(26, 100) => "Gold",
var orders when orders.IsGreaterThan(100) => "Platinum",
_ => "Unknown"
};
}
}
Configuration Management
public class ConfigurationSettings
{
private int _timeout = 30;
private double _retryMultiplier = 1.5;
public int Timeout
{
get => _timeout;
set
{
value.EnsureInRange(1, 300, nameof(Timeout));
_timeout = value;
}
}
public double RetryMultiplier
{
get => _retryMultiplier;
set
{
value.EnsureInRange(1.0, 5.0, nameof(RetryMultiplier));
_retryMultiplier = value;
}
}
public bool IsValidConfiguration()
{
return _timeout.IsBetween(1, 300) &&
_retryMultiplier.IsBetween(1.0, 5.0);
}
}
Performance Considerations
- All operations use the type's built-in
CompareTomethod - No boxing for value types when using generic constraints
- Efficient inline operations for hot paths
- Minimal overhead compared to direct comparison operations
Thread Safety
- All extension methods are thread-safe
- Comparison operations are stateless
- Safe for concurrent usage across multiple threads
Best Practices
- Type Constraints: Ensure types implement
IComparable<T>for compile-time safety - Range Validation: Use
EnsureInRangefor parameter validation - Fluent API: Chain comparison operations for readable code
- Performance: Use appropriate comparison methods based on frequency
- Null Safety: Be aware that nullable types require null checks before comparison