Install
dotnet add package C0deGeek.ObjectCompare --version 1.0.1
| Product | Versions |
|---|---|
| .NET |
README
ObjectComparison
A high-performance, extensible .NET library for deep object comparison with support for complex types, collections, and dynamic objects.
Features
- Deep comparison of objects with circular reference detection
- Object snapshot capability with intelligent cloning
- Support for custom comparison and cloning logic
- Collection comparison with order-sensitive and order-insensitive modes
- Dynamic object comparison support (ExpandoObject, DynamicObject)
- Floating-point comparison with configurable precision
- Advanced type handling:
- Nullable types
- Value types with safe default initialization
- Reference types with proper null handling
- Custom type cloning
- Thread-safe caching:
- Type metadata caching
- Expression compilation caching
- Property access optimization
- Comprehensive comparison results with detailed differences
- Expression tree-based property access for performance
- Support for private field comparison
- Configurable maximum depth and object count limits
Installation
dotnet add package ObjectComparison
Quick Start
// Create a comparer with default configuration
var comparer = new ObjectComparer();
// Compare two objects
var result = comparer.Compare(obj1, obj2);
if (!result.AreEqual)
{
foreach (var difference in result.Differences)
{
Console.WriteLine(difference);
}
}
// Take a snapshot for later comparison
var snapshot = comparer.TakeSnapshot(originalObject);
Object Snapshots
The library provides powerful snapshot capabilities with intelligent cloning.
Basic Snapshot Usage
// Create an object and take a snapshot
var person = new Person
{
Name = "John Doe",
Age = 30,
Hobbies = new List<string> { "Reading", "Gaming" }
};
var comparer = new ObjectComparer();
var snapshot = comparer.TakeSnapshot(person);
// Make changes to the original object
person.Age = 31;
person.Hobbies.Add("Cooking");
// Compare current state with snapshot
var result = comparer.Compare(person, snapshot);
Custom Type Cloning
var config = new ComparisonConfig
{
CustomCloners = new Dictionary<Type, Func<object, object>>
{
{
typeof(DateTime),
obj => ((DateTime)obj).Date // Clone only the date part
},
{
typeof(StringBuilder),
obj => new StringBuilder(((StringBuilder)obj).ToString())
}
}
};
var comparer = new ObjectComparer(config);
Type Handling
// Value Types
struct Point { public int X, Y; }
var point = new Point { X = 1, Y = 2 };
var snapshot = comparer.TakeSnapshot(point); // Creates proper value copy
// Nullable Types
int? nullableValue = 42;
var snapshot = comparer.TakeSnapshot(nullableValue); // Preserves null state
// Collections
var list = new List<Point> { new() { X = 1, Y = 2 } };
var snapshot = comparer.TakeSnapshot(list); // Deep clones elements
Advanced Configuration
var config = new ComparisonConfig
{
// Comparison Options
ComparePrivateFields = true,
DeepComparison = true,
IgnoreCollectionOrder = true,
// Precision Settings
DecimalPrecision = 4,
FloatingPointTolerance = 1e-10,
UseRelativeFloatingPointComparison = true,
// Null Handling
NullValueHandling = NullHandling.Loose,
// Performance Options
MaxDepth = 10,
MaxObjectCount = 10000,
UseCachedMetadata = true,
// Property Options
ExcludedProperties = new HashSet<string> { "CachedValue", "LastModified" },
CompareReadOnlyProperties = true,
// Type-Specific Options
CustomComparers = new Dictionary<Type, ICustomComparer>(),
CollectionItemComparers = new Dictionary<Type, IEqualityComparer>(),
// Diagnostics
TrackPropertyPaths = true,
Logger = loggerFactory.CreateLogger<ObjectComparer>()
};
Error Handling
try
{
var result = comparer.Compare(obj1, obj2);
}
catch (ComparisonException ex) when (ex.Path != null)
{
Console.WriteLine($"Comparison failed at path: {ex.Path}");
Console.WriteLine($"Error: {ex.Message}");
}
catch (ArgumentException ex)
{
Console.WriteLine($"Invalid argument: {ex.Message}");
}
Common Exceptions
ComparisonException: General comparison failuresArgumentException: Invalid types or configurationsInvalidOperationException: Operation not supported for typeNotSupportedException: Unsupported type or operation
Performance Optimization
Caching
// Enable all caching features
var config = new ComparisonConfig
{
UseCachedMetadata = true,
TrackPropertyPaths = false // Disable for better performance
};
// Pre-compile frequently used types
TypeCache.GetMetadata(typeof(MyFrequentType), true);
Memory Management
// Limit object graph traversal
var config = new ComparisonConfig
{
MaxDepth = 5,
MaxObjectCount = 1000,
ExcludedProperties = new HashSet<string> { "LargeCollection", "CachedData" }
};
Collection Handling
// Fast collection comparison for simple types
var config = new ComparisonConfig
{
IgnoreCollectionOrder = true, // Uses optimized comparison
CollectionItemComparers = new Dictionary<Type, IEqualityComparer>
{
{ typeof(int), EqualityComparer<int>.Default }
}
};
Thread Safety
The library uses several thread-safe mechanisms:
- Concurrent collections for caches
- Immutable configuration objects
- Thread-local contexts for comparison state
- Lock-free algorithms where possible
Safe Concurrent Usage
// Create once and reuse
private static readonly ObjectComparer SharedComparer = new();
// Safe for concurrent use
public async Task CompareAsync(object obj1, object obj2)
{
await Task.Run(() => SharedComparer.Compare(obj1, obj2));
}
Best Practices
- Reuse
ObjectComparerinstances - Configure appropriate depth limits
- Use custom comparers for complex types
- Enable caching for repeated comparisons
- Handle circular references appropriately
- Use type-specific equality comparers
- Consider memory usage with large object graphs
- Implement proper error handling
- Use logging for troubleshooting
- Test with representative data sets
Contributing
- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
License
This project is licensed under the MIT License - see the LICENSE.md file for details.
Changes from Previous Version
No structural changes between v1.0.0 and v1.0.1.
Adoption Assessment
Assess this package against its supported frameworks, license, maintenance signals and dependency graph before making it a project standard.
Compatibility metadata lists 1 target framework; confirm the exact target used by your application.
Related Guides & Workflows
Learn how to evaluate, integrate, or audit this package in your .NET project.