c# - Is it safe to use GetHashCode to compare identical Anonymous types? -


given 2 identical anonymous type objects:

{msg:"hello"} //anontype1 {msg:"hello"} //anontype2 

and assume haven't resolved same type (e.g. might defined in different assemblies)

anontype1.equals(anontype2); //false 

furthermore, assume @ compile time, can't structure of 1 (say anontype1) because api exposes object

so, compare them, thought of following techniques:

  1. use reflection msg property on anontype1 comparison.
  2. cast anontype1 dynamic type , reference .msg on dynamic member comparison
  3. compare result of .gethashcode() on each object.

my question is: safe use option 3? i.e. sensible assume .gethashcode() implementation return same value indentically-structured, different anonymous types in current , future versions of .net framework?

interesting question. specification defines equals , gethashcode (note typo in specification!) methods behave instances of same type, implementation not defined. happens, current ms c# compiler implements using magic numbers seed of -1134271262 , multiplier of -1521134295. is not part of specification. theoretically change radically between c# compiler versions , still meet needs to. if 2 assemblies not compiled same compiler, there no guarantee. indeed, "valid" (but unlikely) compiler think new seed value every time compiles.

personally, @ using il or expression techniques this. comparing similarly-shaped objects member-wise name easy expression.

for info, i've looked @ how mcs (the mono compiler) implements gethashcode, , it different; instead of seed , multiplier, uses combination of seed, xor, multiplier, shifts , additions. same type compiled microsoft , mono have very different gethashcode.

static class program {     static void main() {         var obj = new { = "abc", b = 123 };         system.console.writeline(obj.gethashcode());     } } 
  • mono: -2077468848
  • microsoft: -617335881

basically, not think can guarantee this.


how about:

using system; using system.linq; using system.linq.expressions; using system.reflection; class foo {     public string { get; set; }     public int b; // note field!     static void main()     {         var obj1 = new { = "abc", b = 123 };         var obj2 = new foo { = "abc", b = 123 };         console.writeline(memberwisecomparer.areequivalent(obj1, obj2)); // true          obj1 = new { = "abc", b = 123 };         obj2 = new foo { = "abc", b = 456 };         console.writeline(memberwisecomparer.areequivalent(obj1, obj2)); // false          obj1 = new { = "def", b = 123 };         obj2 = new foo { = "abc", b = 456 };         console.writeline(memberwisecomparer.areequivalent(obj1, obj2)); // false     }  }  public static class memberwisecomparer {     public static bool areequivalent(object x, object y)     {         // deal 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);         }     } } 

Comments

Popular posts from this blog

php - cannot display multiple markers in google maps v3 from traceroute result -

c# - DetailsView in ASP.Net - How to add another column on the side/add a control in each row? -

javascript - firefox memory leak -