1

So I have a value object, (arbitrarily say money), and I want to implement equality for it. I am aware of the intended/default behavior for == and .Equals() (reference and data equality).

In this case though, I want to be able to compare two objects, and say that they are equivalent for calculations (e.g. 1m and 3 ft are equivalent) however for persistence (using NHibernate, where isDirty I think depends on equality), user display and selection of currency, I want them to be considered different.

Should I, in this case,

  1. Have different behavior for == and .Equals() (and which should do what),
  2. Wherever I want to check equivalency, just check each property (means extra code)
  3. Implement a method like .IsEquivalent() (I'd prefer not to do the latter)
  4. Something else that I'm missing

Is there a best practice/pattern I should follow? Thanks

Edit: i got some responses regarding changing exchange rates. so updating for clarity. lets say height, and not currency

  • I'd like to clarify, some assumptions:
  • // ignore: Value object contains decimal amount, string / class currency
  • // ignore: Exchange rates do not change.
  • // ignore: the class currency is aware of its exhange rate to and from the other
  • value object contains decimal qty, string / class unit
  • the class unit is aware of its conversion to and from the other
  • i do not intend to expand rates / conversions etc

I'm more concerned about practices and patterns, as opposed to implementing currency. Basically, the same approach to a person's height, where height is a value object, ({1,m} to {3,ft}, where 1m is always "equal"/"equivalent" to 3ft)

Dawood Moazzem
  • 163
  • 3
  • 11
  • Go with 3) it's far cleaner than 1). You also need to pass in an object that represents exchange prices for different currencies, so this doesn't even fit the method signature of `==` or `Equals`. – CodesInChaos Jan 24 '13 at 17:15

3 Answers3

5

I would not treat 1.0 USD as equal to 0.63 GBP. In order to check for equality of monitary value, you'd need more information than just the two values - you'd also need the current exchange rate, etc. This is especially true, as the same two values won't be equal consistently, and equality should always be true if the two values are ever equal.

As such, a method, such as AreEquivalentMonitaryValues(), seems appropriate - especially given that extra info is required.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • actually the value object is {1,USD} and {0.63,GBP} – Dawood Moazzem Jan 24 '13 at 17:23
  • @DawoodMoazzem And those two are not **equal** - while they may (at this point in time) equate to the same value in some methodologies, equality should be a construct so that equal values are always equal. – Reed Copsey Jan 24 '13 at 17:28
  • 1
    i see. what about say not currency,but height. where 1m is always = 3ft – Dawood Moazzem Jan 24 '13 at 17:31
  • 1
    @DawoodMoazzem I would still treat this as equal either via a method or a specific comparer (though 1m != 3ft :) ) - If the unit of measure is actually part of the value, I would not treat them as equal, as equality suggests you could swap them out interchangably *with no impact*, and in this case, the impact is you'd lose your unit of measure. – Reed Copsey Jan 24 '13 at 17:35
  • 1m != 3ft. you're right, i was too lazy to check. so if i want no impact in some cases, but impact in others, i should stick with equals checking all properties, and for the cases that should check equivalence: have an IsEquivalent method? Thanks! – Dawood Moazzem Jan 24 '13 at 17:42
  • @DawoodMoazzem That'd be my suggestion. Using a method or custom comparison type makes more sense, since they objects aren't "completely equal" – Reed Copsey Jan 24 '13 at 17:46
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/23323/discussion-between-dawood-moazzem-and-reed-copsey) – Dawood Moazzem Jan 24 '13 at 17:49
3

Since you want different definitions of equality depending on the context you'd want to use an IEqualityComparer.

As Reed suggests equality for the type itself should really mean, "always and forever are equal" rather than equal for the current exchange rates, but having an IEqualityComparer just means that, "from the point of view of this comparer, they are equal". From there you can have your ExchangeRate type, or something that is given an exchange rate, be able to create an IEqualityComparer<Money> object that represents equality for a given exchange rate. That equality comparer can then be used to compare various types of currencies for equality.

Another approach entirely would be to create an "invariant currency", and give your class ToInvariant and FromInvariant methods so that non-invariant currencies are not equal (ever) and invariant currencies can be equal despite the currency that generated the invariant value.

Community
  • 1
  • 1
Servy
  • 202,030
  • 26
  • 332
  • 449
  • As an alternative, how about defining a wrapper class for the object which contains the desired exchange rate? If one has an immutable "exchange rate snapshot" class with factory methods to wrap currency-containing objects, one could then overload operators to work on matching wrappers (attempting to use, e.g., "+" or "<" on two wrappers which weren't generated by the same factory would throw an exception). It might be cleaner to statically confirm that operations were only performed on compatible wrappers, but I don't know any way of doing that. – supercat Feb 21 '14 at 18:42
0

Create a class MoneyExchangeRates which has a method IsWorthApproximatelyTheSame(Money m1, Money m2).

Exchange rates vary over time, don't make them global mutable state.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262