Correctly implementing structural equality involves a lot of code. My macro implements this for you.
Watch the Screencast
using System;
using System.Console;
using Nemerle.Utility;
module Program
{
[Record]
public class Location
public Latitude : double;
public Longitude : double;
public override Equals(other : object) : bool
match (other)
| null => false
| l is Location => l.Latitude == Latitude && l.Longitude == Longitude
| _ => false;
}
public override GetHashCode() : int
Latitude.GetHashCode() ^ Longitude.GetHashCode()
public static @==(l1 : Location, l2 : Location) : bool
def l1IsNull = object.ReferenceEquals(l1, null);
def l2IsNull = object.ReferenceEquals(l2, null);
match (l1IsNull, l2IsNull)
| (true, true) => true
| (false, false) => l1.Equals(l2)
| _ => false
public static @!=(l1 : Location, l2 : Location) : bool
!(l1 == l2)
Main() : void
def l1 = Location(30, 70);
def l2 = Location(30, 70);
def l3 = Location(40, 90);
WriteLine("l1.Equals(l2) -> {0}", l1.Equals(l2));
WriteLine("l1 == l2 -> {0}", l1 == l2);
WriteLine("l1 != l2 -> {0}", l1 != l2);
WriteLine("l1.GetHashCode() == l2.GetHashCode() -> {0}", l1.GetHashCode() == l2.GetHashCode());
WriteLine("l1 != l3 -> {0}", l1 != l3);
_ = ReadLine();
using SampleMacros;
[ImplementEquality(Latitude, Longitude)]
using Nemerle;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using Nemerle.Macros;
namespace SampleMacros
[MacroUsage(MacroPhase.BeforeInheritance, MacroTargets.Class)]
public macro ImplementEquality(typeBuilder : TypeBuilder, params members : list[PExpr])
ImplementEqualityModule.AddEqualsMethod(typeBuilder, members);
ImplementEqualityModule.AddEqualityOperator(typeBuilder);
ImplementEqualityModule.AddInequalityOperator(typeBuilder);
module ImplementEqualityModule
public AddEqualsMethod(typeBuilder : TypeBuilder, members : list[PExpr]) : void
def other = Macros.NewSymbol();
def comparisons = members.Map(m => {
def s = Splicable.Name(Name(m.ToString()));
<[ (this.$s).Equals($(other : name).$s) ]>
});
def b = comparisons.Tail.FoldLeft(comparisons.Head, (c, acc) => <[ $acc && $c ]>);
typeBuilder.Define(<[ decl:
public override Equals(obj : object) : bool
match (obj) {
| $(other : name) is $(typeBuilder.ParsedTypeName) => $b
]>);
def hashCodes = members.Map(m => { def s = Splicable.Name(Name(m.ToString())); <[ this.$s.GetHashCode() ]> });
def hashCode = hashCodes.Tail.FoldLeft(hashCodes.Head, (c, acc) => <[ $acc ^ $c ]>);
$hashCode
public AddEqualityOperator(typeBuilder : TypeBuilder) : void
typeBuilder.Define(CreateEqualityOperator(typeBuilder.ParsedTypeName, false));
public AddInequalityOperator(typeBuilder : TypeBuilder) : void
typeBuilder.Define(CreateEqualityOperator(typeBuilder.ParsedTypeName, true));
CreateEqualityOperator(typeRef : PExpr, invert : bool) : ClassMember.Function
def op = Splicable.Name(Name(if (invert) "!=" else "=="));
<[ decl:
public static $op(p1 : $(typeRef), p2 : $(typeRef)) : bool
def p1Null = object.ReferenceEquals(null, p1);
def p2Null = object.ReferenceEquals(null, p2);
match (p1Null, p2Null) {
| (false, false) => $(if (invert) <[false == p1.Equals(p2)]> else <[p1.Equals(p2)]> )
]>
Powered by: newtelligence dasBlog 2.2.8279.16125
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.
© Copyright 2010, Andrew Davey
E-mail