Linger.Utils
📝 View this document in: English | 中文
A comprehensive .NET utility library providing extensive type conversion extensions, string manipulation utilities, date/time helpers, file system operations, collection extensions, and various helper classes for everyday development tasks.
Overview
Linger.Utils offer a rich collection of extension methods and helper classes that make common programming tasks simpler and more efficient. The library follows modern C# coding practices and supports multiple .NET framework versions.
Table of Contents
Features
🚀 Core Extensions
- String Extensions: Rich string operations, validation, conversion, and formatting utilities
- DateTime Extensions: Date and time manipulation, formatting, and calculations
- Numeric Extensions: Type-safe numeric conversions and operations
- Enum Extensions: Enhanced enum handling and conversion
- Object Extensions: General object operations and validation
- Array Extensions: Array processing and manipulation utilities
- GUID Extensions: GUID operation and validation utilities
📦 Collection Extensions
- List Extensions: Enhanced list operations and processing
- Collection Extensions: General collection utilities and transformations
💾 Data Extensions
- DataTable Extensions: DataTable operation utilities
- Data Conversion: Safe data type conversion and transformation
📁 File System Operations
- File Helper: Comprehensive file operations (read, write, copy, move, delete)
- Path Helper: Cross-platform path operations and validation
- Directory Operations: Directory management and traversal utilities
🔧 Helper Classes
- Expression Helper: Expression tree operations and utilities
- Retry Helper: Robust retry mechanisms for operations
- Property Helper: Reflection-based property operations
- GUID Code: Enhanced GUID generation and operations
- OS Platform Helper: Cross-platform operating system detection
- Parameter Validation Extensions: Defensive programming and input validation utilities
🌐 JSON Support
- JSON Extensions: Simplified JSON serialization and deserialization
- Custom Converters: Specialized JSON converters for complex types
Installation
dotnet add package Linger.Utils
Target Frameworks
- .NET 9.0
- .NET 8.0
- .NET Standard 2.0
- .NET Framework 4.7.2
Quick Start
String Extensions
using Linger.Extensions.Core;
string email = "user@example.com";
bool isValid = email.IsEmail();
string number = "123";
int result = number.ToInt(0);
int? nullableResult = number.ToIntOrNull();
string text = " Hello World ";
string cleaned = text.Trim();
string longText = "Hello World";
string leftPart = longText.Left(5);
string rightPart = longText.Right(5);
string part = longText.SafeSubstring(0, 20);
bool isEmpty = text.IsNullOrEmpty();
bool isNumber = number.IsNumber();
bool isInt = number.IsInteger();
DateTime Extensions
using Linger.Extensions.Core;
DateTime date = DateTime.Now;
DateTime birthDate = new DateTime(1990, 5, 15);
int age = birthDate.CalculateAge();
bool isInRange = date.InRange(DateTime.Today, DateTime.Today.AddDays(7));
DateTime startOfDay = date.StartOfDay();
DateTime endOfDay = date.EndOfDay();
DateTime startOfMonth = date.StartOfMonth();
DateTime endOfMonth = date.EndOfMonth();
File Operations
using Linger.Helper;
FileHelper.WriteText("data.txt", "Hello World");
string content = FileHelper.ReadText("data.txt");
FileHelper.CopyFile("source.txt", "backup/dest.txt");
FileHelper.DeleteFileIfExists("temp.txt");
FileHelper.EnsureDirectoryExists("logs/2024");
Collection Extensions
using Linger.Extensions.Collection;
var list = new List<int> { 1, 2, 3, 4, 5 };
bool isEmpty = list.IsNullOrEmpty();
var pagedResult = list.Paging(2, 2);
string result = list.ToSeparatedString(", ");
list.ForEach(Console.WriteLine);
var dataTable = list.Select(x => new { Value = x }).ToDataTable();
Object Extensions
using Linger.Extensions.Core;
object obj = GetSomeObject();
string result = obj.ToSafeString("default");
string stringValue = obj.ToString();
bool isNumber = stringValue.IsNumber();
bool isInt = stringValue.IsInteger();
bool isDouble = stringValue.IsDouble();
var stringRepresentation = obj.ToStringOrNull();
int value = 5;
bool inRange = value.InRange(1, 10);
JSON Extensions
using Linger.Extensions;
var user = new { Name = "John", Age = 30 };
string json = user.ToJsonString();
var userObj = json.Deserialize<User>();
dynamic dynamicObj = json.DeserializeDynamicJsonObject();
string name = dynamicObj.Name;
string jsonArray = "[{\"Name\":\"John\",\"Age\":30}]";
DataTable? dataTable = jsonArray.ToDataTable();
GUID Extensions
using Linger.Extensions.Core;
Guid guid = Guid.NewGuid();
bool isEmpty = guid.IsEmpty();
bool isNotEmpty = guid.IsNotEmpty();
Guid? nullableGuid = null;
bool isNull = nullableGuid.IsNull();
bool isNotNull = nullableGuid.IsNotNull();
bool isNullOrEmpty = nullableGuid.IsNullOrEmpty();
bool isNotNullAndEmpty = nullableGuid.IsNotNullAndEmpty();
long longValue = guid.ToInt64();
int intValue = guid.ToInt32();
#if NET9_0_OR_GREATER
DateTimeOffset timestamp = guid.GetTimestamp();
#endif
Array Extensions
using Linger.Extensions.Core;
int[] numbers = { 1, 2, 3, 4, 5 };
numbers.ForEach(n => Console.WriteLine(n));
numbers.ForEach((n, index) => Console.WriteLine($"Index {index}: {n}"));
Enum Extensions
using Linger.Extensions.Core;
public enum Status
{
Active = 1,
Inactive = 2,
Pending = 3
}
string statusName = "Active";
Status status = statusName.GetEnum<Status>();
int statusValue = 1;
Status statusFromInt = statusValue.GetEnum<Status>();
string enumName = statusValue.GetEnumName<Status>();
string description = status.GetDescription();
Parameter Validation
using Linger.Helper;
public void ProcessData(string data, IEnumerable<int> numbers, string filePath)
{
data.EnsureIsNotNull(nameof(data));
data.EnsureIsNotNullAndEmpty(nameof(data));
data.EnsureIsNotNullAndWhiteSpace(nameof(data));
numbers.EnsureIsNotNullOrEmpty(nameof(numbers));
filePath.EnsureFileExist(nameof(filePath));
Path.GetDirectoryName(filePath).EnsureDirectoryExist();
(data.Length > 0).EnsureIsTrue(nameof(data), "Data must not be empty");
(numbers.Count() < 1000).EnsureIsTrue(nameof(numbers), "Too many items");
int value = 5;
value.EnsureIsInRange(1, 10, nameof(value));
object? obj = GetSomeObject();
obj.EnsureIsNotNull(nameof(obj));
obj.EnsureIsNull(nameof(obj));
}
Advanced Features
Retry Helper
using Linger.Helper;
var options = new RetryOptions
{
MaxRetries = 3,
BaseDelayMs = 1000
};
var retryHelper = new RetryHelper(options);
var result = await retryHelper.ExecuteAsync(
async () => await SomeOperationThatMightFail(),
"Operation Name"
);
var defaultRetryHelper = new RetryHelper();
var result2 = await defaultRetryHelper.ExecuteAsync(
async () => await AnotherOperationThatMightFail(),
"Another Operation Name"
);
Expression Helper
using Linger.Helper;
using Linger.Enums;
Expression<Func<User, bool>> trueExpression = ExpressionHelper.True<User>();
Expression<Func<User, bool>> falseExpression = ExpressionHelper.False<User>();
Expression<Func<User, bool>> ageFilter = ExpressionHelper.CreateGreaterThan<User>("Age", "18");
Expression<Func<User, bool>> nameFilter = ExpressionHelper.GetContains<User>("Name", "John");
var conditions = new List<Condition>
{
new Condition { Field = "Age", Op = CompareOperator.GreaterThan, Value = 18 },
new Condition { Field = "Name", Op = CompareOperator.Contains, Value = "John" }
};
Expression<Func<User, bool>> complexFilter = ExpressionHelper.BuildLambda<User>(conditions);
Path Operations
using Linger.Helper.PathHelpers;
string messyPath = @"C:\temp\..\folder\.\file.txt";
string normalized = StandardPathHelper.NormalizePath(messyPath);
string path1 = @"C:\Users\Documents\file.txt";
string path2 = @"c:\users\documents\FILE.TXT";
bool pathEquals = StandardPathHelper.PathEquals(path1, path2);
string basePath = @"C:\Projects\MyApp";
string targetPath = @"C:\Projects\MyApp\src\Components\Button.cs";
string relative = StandardPathHelper.GetRelativePath(basePath, targetPath);
string workingDir = @"C:\Projects";
string relativePath = @"MyApp\src\file.txt";
string absolutePath = StandardPathHelper.ResolveToAbsolutePath(workingDir, relativePath);
string suspiciousPath = "file<name>.txt";
bool hasInvalidChars = StandardPathHelper.ContainsInvalidPathChars(suspiciousPath);
string filePath = @"C:\temp\data.txt";
bool fileExists = StandardPathHelper.Exists(filePath, checkAsFile: true);
bool dirExists = StandardPathHelper.Exists(filePath, checkAsFile: false);
string deepPath = @"C:\Projects\MyApp\src\Components\Button.cs";
string parentDir = StandardPathHelper.GetParentDirectory(deepPath, levels: 1);
string grandParentDir = StandardPathHelper.GetParentDirectory(deepPath, levels: 2);
Best Practices
- Use Safe Methods: Prefer
ToIntOrNull()
over ToInt()
when conversion might fail
- Null Checking: Use extension methods like
IsNullOrEmpty()
for validation
- Parameter Validation: Use
GuardExtensions
methods like EnsureIsNotNull()
, EnsureIsNotNullAndEmpty()
for input validation
- Leverage Async: Use async versions of file operations for better performance
- Error Handling: Always handle potential exceptions in file operations
- Resource Management: Use
using
statements for disposable resources
- GUID Operations: Use extension methods like
IsEmpty()
and IsNotEmpty()
instead of direct comparison
- Collection Processing: Use
ForEach()
extension methods to simplify array and collection iteration
Dependencies
The library has minimal external dependencies:
- System.Text.Json (for JSON operations)
- System.Data.DataSetExtensions (for .NET Framework and .NET Standard 2.0)
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. Please ensure:
- Follow existing code style
- Add unit tests for new features
- Update documentation as needed
License
This project is licensed under the terms of the license provided with the Linger project.
For more information about the Linger framework and other related packages, visit the Linger Project Repository.