My favorites | Sign in
Project Home
Checkout   Browse   Changes    
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using System.Reflection;
using System.Diagnostics;
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using System.Xml.Serialization;

namespace SharpArch.Core.DomainModel
{
/// <summary>
/// Provides a standard base class for facilitating comparison of objects.
///
/// For a discussion of the implementation of Equals/GetHashCode, see
/// http://devlicio.us/blogs/billy_mccafferty/archive/2007/04/25/using-equals-gethashcode-effectively.aspx
/// and http://groups.google.com/group/sharp-architecture/browse_thread/thread/f76d1678e68e3ece?hl=en for
/// an in depth and conclusive resolution.
/// </summary>
[Serializable]
[JsonObject(MemberSerialization.OptIn)]
public abstract class BaseObject
{
public override bool Equals(object obj) {
BaseObject compareTo = obj as BaseObject;

if (ReferenceEquals(this, compareTo))
return true;

return compareTo != null && GetType().Equals(compareTo.GetTypeUnproxied()) &&
HasSameObjectSignatureAs(compareTo);
}

/// <summary>
/// This is used to provide the hashcode identifier of an object using the signature
/// properties of the object; although it's necessary for NHibernate's use, this can
/// also be useful for business logic purposes and has been included in this base
/// class, accordingly. Since it is recommended that GetHashCode change infrequently,
/// if at all, in an object's lifetime, it's important that properties are carefully
/// selected which truly represent the signature of an object.
/// </summary>
public override int GetHashCode() {
unchecked {
IEnumerable<PropertyInfo> signatureProperties = GetSignatureProperties();

// It's possible for two objects to return the same hash code based on
// identically valued properties, even if they're of two different types,
// so we include the object's type in the hash calculation
int hashCode = GetType().GetHashCode();

foreach (PropertyInfo property in signatureProperties) {
object value = property.GetValue(this, null);

if (value != null)
hashCode = (hashCode * HASH_MULTIPLIER) ^ value.GetHashCode();
}

if (signatureProperties.Any())
return hashCode;

// If no properties were flagged as being part of the signature of the object,
// then simply return the hashcode of the base object as the hashcode.
return base.GetHashCode();
}
}

/// <summary>
/// You may override this method to provide your own comparison routine.
/// </summary>
public virtual bool HasSameObjectSignatureAs(BaseObject compareTo) {
IEnumerable<PropertyInfo> signatureProperties = GetSignatureProperties();

foreach (PropertyInfo property in signatureProperties) {
object valueOfThisObject = property.GetValue(this, null);
object valueToCompareTo = property.GetValue(compareTo, null);

if (valueOfThisObject == null && valueToCompareTo == null)
continue;

if ((valueOfThisObject == null ^ valueToCompareTo == null) ||
(!valueOfThisObject.Equals(valueToCompareTo))) {
return false;
}
}

// If we've gotten this far and signature properties were found, then we can
// assume that everything matched; otherwise, if there were no signature
// properties, then simply return the default bahavior of Equals
return signatureProperties.Any() || base.Equals(compareTo);
}

/// <summary>
/// </summary>
public virtual IEnumerable<PropertyInfo> GetSignatureProperties() {
IEnumerable<PropertyInfo> properties;

// Init the signaturePropertiesDictionary here due to reasons described at
// http://blogs.msdn.com/jfoscoding/archive/2006/07/18/670497.aspx
if (signaturePropertiesDictionary == null)
signaturePropertiesDictionary = new Dictionary<Type, IEnumerable<PropertyInfo>>();

if (signaturePropertiesDictionary.TryGetValue(GetType(), out properties))
return properties;

return (signaturePropertiesDictionary[GetType()] = GetTypeSpecificSignatureProperties());
}

/// <summary>
/// When NHibernate proxies objects, it masks the type of the actual entity object.
/// This wrapper burrows into the proxied object to get its actual type.
///
/// Although this assumes NHibernate is being used, it doesn't require any NHibernate
/// related dependencies and has no bad side effects if NHibernate isn't being used.
///
/// Related discussion is at http://groups.google.com/group/sharp-architecture/browse_thread/thread/ddd05f9baede023a ...thanks Jay Oliver!
/// </summary>
protected virtual Type GetTypeUnproxied() {
return GetType();
}

/// <summary>
/// Enforces the template method pattern to have child objects determine which specific
/// properties should and should not be included in the object signature comparison. Note
/// that the the BaseObject already takes care of performance caching, so this method
/// shouldn't worry about caching...just return the goods man!
/// </summary>
protected abstract IEnumerable<PropertyInfo> GetTypeSpecificSignatureProperties();

/// <summary>
/// This static member caches the domain signature properties to avoid looking them up for
/// each instance of the same type.
///
/// A description of the very slick ThreadStatic attribute may be found at
/// http://www.dotnetjunkies.com/WebLog/chris.taylor/archive/2005/08/18/132026.aspx
/// </summary>
[ThreadStatic]
private static Dictionary<Type, IEnumerable<PropertyInfo>> signaturePropertiesDictionary;

/// <summary>
/// To help ensure hashcode uniqueness, a carefully selected random number multiplier
/// is used within the calculation. Goodrich and Tamassia's Data Structures and
/// Algorithms in Java asserts that 31, 33, 37, 39 and 41 will produce the fewest number
/// of collissions. See http://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/
/// for more information.
/// </summary>
private const int HASH_MULTIPLIER = 31;
}
}

Change log

r476 by wmccafferty on Jun 16, 2009   Diff
Exposed
BaseObject.HasSameObjectSignatureAs() as
public instead of protected to resolve
http://code.google.com/p/sharp-
architecture/issues/detail?id=99 .  Had to
change the visibility of this overridden
method within Order.cs, accordingly.
Go to: 
Project members, sign in to write a code review

Older revisions

r464 by wmccafferty on May 25, 2009   Diff
Modified the hashcode multipler used
by BaseObject.cs and Entity.cs to be
31 based on information found at http:
//computinglife.wordpress.com/2008/11/
20/why-do-hash-functions-use-prime-
...
r379 by wmccafferty on Mar 7, 2009   Diff
* In SharpArch.Core, moved
GetTypeUnproxied() from Entity to
BaseObject
* In Northwind.Web/Global.asax.cs,
moved NHibernate.Init() to
...
r308 by wmccafferty on Jan 12, 2009   Diff
[No log message]
All revisions of this file

File info

Size: 7054 bytes, 147 lines
Powered by Google Project Hosting