1 //------------------------------------------------------------------------------
2 // <copyright file="DataRowComparer.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">spather</owner>
7 //------------------------------------------------------------------------------
10 using System.Collections;
11 using System.Collections.Generic;
12 using System.Diagnostics;
13 using System.Data.DataSetExtensions;
18 /// This class implements IEqualityComparer using value based semantics
19 /// when comparing DataRows.
21 public static class DataRowComparer
24 /// Gets the singleton instance of the data row comparer.
26 public static DataRowComparer<DataRow> Default { get { return DataRowComparer<DataRow>.Default; } }
28 internal static bool AreEqual(object a, object b)
30 if (Object.ReferenceEquals(a, b))
31 { // same reference or (null, null) or (DBNull.Value, DBNull.Value)
34 if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(a, DBNull.Value) ||
35 Object.ReferenceEquals(b, null) || Object.ReferenceEquals(b, DBNull.Value))
36 { // (null, non-null) or (null, DBNull.Value) or vice versa
39 return (a.Equals(b) || (a.GetType().IsArray && CompareArray((Array)a, b as Array)));
42 private static bool AreElementEqual(object a, object b)
44 if (Object.ReferenceEquals(a, b))
45 { // same reference or (null, null) or (DBNull.Value, DBNull.Value)
48 if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(a, DBNull.Value) ||
49 Object.ReferenceEquals(b, null) || Object.ReferenceEquals(b, DBNull.Value))
50 { // (null, non-null) or (null, DBNull.Value) or vice versa
56 private static bool CompareArray(Array a, Array b)
61 (a.Length != b.Length))
62 { // automatically consider array's with Rank>1 not-equal
66 int index1 = a.GetLowerBound(0);
67 int index2 = b.GetLowerBound(0);
68 if (a.GetType() == b.GetType() && (0 == index1) && (0 == index2))
70 switch (Type.GetTypeCode(a.GetType().GetElementType()))
73 return DataRowComparer.CompareEquatableArray<Byte>((Byte[])a, (Byte[])b);
75 return DataRowComparer.CompareEquatableArray<Int16>((Int16[])a, (Int16[])b);
77 return DataRowComparer.CompareEquatableArray<Int32>((Int32[])a, (Int32[])b);
79 return DataRowComparer.CompareEquatableArray<Int64>((Int64[])a, (Int64[])b);
81 return DataRowComparer.CompareEquatableArray<String>((String[])a, (String[])b);
85 //Compare every element. But don't recurse if we have Array of array.
86 int length = index1 + a.Length;
87 for (; index1 < length; ++index1, ++index2)
89 if (!AreElementEqual(a.GetValue(index1), b.GetValue(index2)))
97 private static bool CompareEquatableArray<TElem>(TElem[] a, TElem[] b) where TElem : IEquatable<TElem>
99 if (Object.ReferenceEquals(a, b))
103 if (Object.ReferenceEquals(a, null) ||
104 Object.ReferenceEquals(b, null))
108 if (a.Length != b.Length)
113 for (int i = 0; i < a.Length; ++i)
115 if (!a[i].Equals(b[i]))
125 /// This class implements IEqualityComparer using value based semantics
126 /// when comparing DataRows.
128 public sealed class DataRowComparer<TRow> : IEqualityComparer<TRow> where TRow : DataRow
131 /// Private constructor to prevent initialization outside of Default singleton instance.
133 private DataRowComparer() { }
135 private static DataRowComparer<TRow> _instance = new DataRowComparer<TRow>();
138 /// Gets the singleton instance of the data row comparer.
140 public static DataRowComparer<TRow> Default { get { return _instance; } }
143 /// This method compares to DataRows by doing a column by column value based
146 /// <param name="leftRow">
147 /// The first input DataRow
149 /// <param name="rightRow">
150 /// The second input DataRow
153 /// True if rows are equal, false if not.
155 public bool Equals(TRow leftRow, TRow rightRow)
157 if (Object.ReferenceEquals(leftRow, rightRow))
161 if (Object.ReferenceEquals(leftRow, null) ||
162 Object.ReferenceEquals(rightRow, null))
167 if (leftRow.RowState == DataRowState.Deleted || rightRow.RowState == DataRowState.Deleted)
169 throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_CannotCompareDeletedRow);
172 int count = leftRow.Table.Columns.Count;
173 if (count != rightRow.Table.Columns.Count)
178 for (int i = 0; i < count; ++i)
180 if (!DataRowComparer.AreEqual(leftRow[i], rightRow[i]))
189 /// This mtheod retrieves a hash code for the source row.
191 /// <param name="row">
192 /// The source DataRow
195 /// HashCode for row based on values in the row.
197 public int GetHashCode(TRow row)
199 DataSetUtil.CheckArgumentNull(row, "row");
201 if (row.RowState == DataRowState.Deleted)
203 throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_CannotCompareDeletedRow);
207 Debug.Assert(row.Table != null);
208 if (row.Table.Columns.Count > 0)
210 // if the row has at least one column, then use the first column value
211 object value = row[0];
213 Type valueType = value.GetType();
214 if (valueType.IsArray)
216 Array array = value as Array;
220 hash = value.GetHashCode();
222 else if (array.Length > 0)
224 hash = array.GetValue(array.GetLowerBound(0)).GetHashCode();
229 System.ValueType vt = value as System.ValueType;
231 // have to unbox value types.
234 hash = vt.GetHashCode();
238 hash = value.GetHashCode();
242 // if table has no columns, the hash code is 0