有趣的問題。該規範定義了Equals
和GetHashcode
(注意規範中的錯字!)方法將針對相同類型的實例運行,但實現未定義。碰巧,當前的MS C#編譯器使用幻數,如-1134271262
的種子和-1521134295
的乘數來實現此功能。但是不是規範的一部分。從理論上講,這可能會在C#編譯器版本之間發生根本性變化,並且仍然能夠滿足需要。所以,如果2個程序集不是由同一個編譯器編譯的,那麼就沒有保證。事實上,編譯器每次編譯時都會想到新的種子值,這是「有效的」(但不太可能)。
就我個人而言,我會看看使用IL或Expression
技術來做到這一點。使用Expression
相當容易地通過名稱比較成員相似形狀的對象。
有關信息,我也看了如何mcs
(Mono的編譯器)實現GetHashCode
,並是不同;而不是種子和乘數,它使用種子,異或,乘數,移位和加數的組合。所以微軟和Mono編譯的同類型將有非常不同GetHashCode
。
static class Program {
static void Main() {
var obj = new { A = "abc", B = 123 };
System.Console.WriteLine(obj.GetHashCode());
}
}
- 單聲道:-2077468848
- 微軟:-617335881
基本上,我不認爲你能保證這一點。
如何:
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);
}
}
}
注:我添加了一個'基於Expression'-成員逐一比較器 - 可能是有用的 – 2013-05-13 09:00:51
輝煌,謝謝。 – 2013-05-13 11:15:59