Câu hỏi thú vị. Đặc điểm kỹ thuật xác định rằng các phương thức Equals
và GetHashcode
(lưu ý lỗi đánh máy trong đặc điểm kỹ thuật!) Sẽ hoạt động đối với các thể hiện cùng loại, tuy nhiên việc triển khai không được xác định. Khi nó xảy ra, trình biên dịch MS C# hiện tại thực hiện điều này bằng cách sử dụng các số ma thuật như một hạt giống của -1134271262
và một số nhân là -1521134295
. Nhưng rằng không phải là một phần của đặc điểm kỹ thuật. Về mặt lý thuyết có thể thay đổi triệt để giữa các phiên bản trình biên dịch C# và nó vẫn sẽ đáp ứng những gì nó cần. Vì vậy, nếu 2 hội đồng không được biên dịch bởi trình biên dịch tương tự, không có bảo đảm. Thật vậy, nó sẽ là "hợp lệ" (nhưng không chắc) cho trình biên dịch để nghĩ ra một giá trị hạt giống mới mỗi khi nó biên dịch.
Cá nhân, tôi sẽ xem xét sử dụng kỹ thuật IL hoặc Expression
để thực hiện việc này. So sánh các đối tượng tương tự như hình tượng thành viên theo tên khá dễ làm với Expression
.
Đối với thông tin, tôi cũng đã xem xét cách mcs
(trình biên dịch Mono) thực hiện GetHashCode
và nó khác; thay vì hạt giống và nhân, nó sử dụng một sự kết hợp của hạt giống, xor, nhân, ca và bổ sung. Vì vậy, cùng loại được biên soạn bởi Microsoft và Mono sẽ có rất khác nhau GetHashCode
.
static class Program {
static void Main() {
var obj = new { A = "abc", B = 123 };
System.Console.WriteLine(obj.GetHashCode());
}
}
- Mono: -2077468848
- Microsoft: -617335881
Về cơ bản, tôi không nghĩ rằng bạn có thể đảm bảo điều này.
Làm thế nào về:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
class Foo
{
public string A { get; set; }
public int B; // note a field!
static void Main()
{
var obj1 = new { A = "abc", B = 123 };
var obj2 = new Foo { A = "abc", B = 123 };
Console.WriteLine(MemberwiseComparer.AreEquivalent(obj1, obj2)); // True
obj1 = new { A = "abc", B = 123 };
obj2 = new Foo { A = "abc", B = 456 };
Console.WriteLine(MemberwiseComparer.AreEquivalent(obj1, obj2)); // False
obj1 = new { A = "def", B = 123 };
obj2 = new Foo { A = "abc", B = 456 };
Console.WriteLine(MemberwiseComparer.AreEquivalent(obj1, obj2)); // False
}
}
public static class MemberwiseComparer
{
public static bool AreEquivalent(object x, object y)
{
// deal with nulls...
if (x == null) return y == null;
if (y == null) return false;
return AreEquivalentImpl((dynamic)x, (dynamic)y);
}
private static bool AreEquivalentImpl<TX, TY>(TX x, TY y)
{
return AreEquivalentCache<TX, TY>.Eval(x, y);
}
static class AreEquivalentCache<TX, TY>
{
static AreEquivalentCache()
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
var xMembers = typeof(TX).GetProperties(flags).Select(p => p.Name)
.Concat(typeof(TX).GetFields(flags).Select(f => f.Name));
var yMembers = typeof(TY).GetProperties(flags).Select(p => p.Name)
.Concat(typeof(TY).GetFields(flags).Select(f => f.Name));
var members = xMembers.Intersect(yMembers);
Expression body = null;
ParameterExpression x = Expression.Parameter(typeof(TX), "x"),
y = Expression.Parameter(typeof(TY), "y");
foreach (var member in members)
{
var thisTest = Expression.Equal(
Expression.PropertyOrField(x, member),
Expression.PropertyOrField(y, member));
body = body == null ? thisTest
: Expression.AndAlso(body, thisTest);
}
if (body == null) body = Expression.Constant(true);
func = Expression.Lambda<Func<TX, TY, bool>>(body, x, y).Compile();
}
private static readonly Func<TX, TY, bool> func;
public static bool Eval(TX x, TY y)
{
return func(x, y);
}
}
}
Lưu ý: tôi đã thêm một 'Expression' dựa trên memberwise Comparer - có thể có ích –
Brilliant, cảm ơn. –