能否用AutoMapper实现对象相等性测试?新增字段自动纳入校验
First off: AutoMapper is not the right tool for this job. AutoMapper’s core purpose is to map properties between different object types, not to implement value-based equality checks for a single class. You could technically hack together something using its mapping configuration to compare properties, but that would be a huge misuse of the library—you’d be fighting against its intended design, and the solution would be fragile, hard to maintain, and perform poorly compared to purpose-built approaches.
Now let’s dive into the best ways to solve your actual requirement: implementing equality checks that automatically include new string fields (and scale to non-string fields later).
Option 1: Reflection-Based Equality (Quick, Flexible, But Less Performant)
If you need a simple, no-compiler-setup solution, you can use reflection to dynamically iterate over all string fields in your Book class and compare them. Here’s a rough implementation:
public class Book { public string Title; public string Author; // New fields like Publisher will be automatically included public override bool Equals(object obj) { if (obj is not Book other) return false; // Get all public instance string fields (adjust BindingFlags for non-public if needed) var stringFields = GetType().GetFields(BindingFlags.Public | BindingFlags.Instance) .Where(f => f.FieldType == typeof(string)); foreach (var field in stringFields) { var value1 = field.GetValue(this); var value2 = field.GetValue(other); if (!string.Equals(value1, value2, StringComparison.Ordinal)) return false; } return true; } public override int GetHashCode() { var hash = new HashCode(); var stringFields = GetType().GetFields(BindingFlags.Public | BindingFlags.Instance) .Where(f => f.FieldType == typeof(string)); foreach (var field in stringFields) { hash.Add(field.GetValue(this) as string); } return hash.ToHashCode(); } }
Pros & Cons:
- ✅ Automatically includes new string fields without code changes
- ✅ Easy to implement right now
- ❌ Reflection has performance overhead (not ideal for high-throughput scenarios)
- ❌ You’ll need to adjust the logic if you add non-string fields (e.g., handle value types, reference types with their own equality checks)
Option 2: Source Generators (Best for Performance & Maintainability)
For a production-grade solution, use .NET Source Generators (available in .NET 6+). This lets you generate compile-time equality code that’s as fast as hand-written, and it automatically updates when you add new fields.
Here’s how to approach it:
- Create a custom attribute (e.g.,
AutoEqualityCheckAttribute) to mark classes that need automatic equality logic. - Write a source generator that scans for classes with this attribute, collects all string fields (or all fields/properties if you want to support non-string later), and generates
EqualsandGetHashCodemethods.
Example generated code for your Book class would look like hand-written equality checks:
public override bool Equals(object obj) { return obj is Book other && string.Equals(Title, other.Title, StringComparison.Ordinal) && string.Equals(Author, other.Author, StringComparison.Ordinal) && string.Equals(Publisher, other.Publisher, StringComparison.Ordinal); // Auto-added when you add the field } public override int GetHashCode() { return HashCode.Combine(Title, Author, Publisher); }
Pros & Cons:
- ✅ Zero runtime overhead (code is generated at compile time)
- ✅ Automatically includes new fields/properties without manual changes
- ✅ Easy to extend to non-string fields (just adjust the generator to handle value types,
IEquatable<T>types, etc.) - ❌ Requires setting up a source generator project (a bit more initial work, but worth it for long-term maintainability)
Option 3: Use C# Records (Simplest for Value Equality)
If you’re able to refactor your Book class into a record type, C# automatically implements value-based equality that includes all properties (you can adjust to use fields too). When you add new properties/fields, the equality check updates automatically.
public record Book(string Title, string Author); // Later add Publisher: public record Book(string Title, string Author, string Publisher);
Pros & Cons:
- ✅ No manual equality code needed—C# handles it all
- ✅ Automatically includes new properties/fields
- ❌ Records are immutable by default (you can use
initproperties for mutable records, but it’s a different pattern than a regular class) - ❌ Less control over equality logic (e.g., custom string comparison) unless you override the generated methods
Handling Non-String Fields
When you need to support non-string fields later:
- For reflection: Extend the logic to check field types—use
object.Equalsfor value types, check if the type implementsIEquatable<T>and call that method, or add custom handling for specific types (e.g.,DateTimewith a specific kind). - For source generators: Update the generator to generate appropriate comparison logic based on field type (e.g.,
intuses==,DateTimeusesDateTime.Equals, reference types use theirEqualsmethod). - For records: C# automatically handles value types and reference types using their default equality checks, but you can override
EqualsandGetHashCodeif you need custom logic.
内容的提问来源于stack exchange,提问作者William Jockusch

