536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
247 lines
8.5 KiB
C#
247 lines
8.5 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="DataRowComparer.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">Microsoft</owner>
|
|
// <owner current="true" primary="false">spather</owner>
|
|
//------------------------------------------------------------------------------
|
|
using System;
|
|
using System.Data;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Data.DataSetExtensions;
|
|
|
|
namespace System.Data
|
|
{
|
|
/// <summary>
|
|
/// This class implements IEqualityComparer using value based semantics
|
|
/// when comparing DataRows.
|
|
/// </summary>
|
|
public static class DataRowComparer
|
|
{
|
|
/// <summary>
|
|
/// Gets the singleton instance of the data row comparer.
|
|
/// </summary>
|
|
public static DataRowComparer<DataRow> Default { get { return DataRowComparer<DataRow>.Default; } }
|
|
|
|
internal static bool AreEqual(object a, object b)
|
|
{
|
|
if (Object.ReferenceEquals(a, b))
|
|
{ // same reference or (null, null) or (DBNull.Value, DBNull.Value)
|
|
return true;
|
|
}
|
|
if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(a, DBNull.Value) ||
|
|
Object.ReferenceEquals(b, null) || Object.ReferenceEquals(b, DBNull.Value))
|
|
{ // (null, non-null) or (null, DBNull.Value) or vice versa
|
|
return false;
|
|
}
|
|
return (a.Equals(b) || (a.GetType().IsArray && CompareArray((Array)a, b as Array)));
|
|
}
|
|
|
|
private static bool AreElementEqual(object a, object b)
|
|
{
|
|
if (Object.ReferenceEquals(a, b))
|
|
{ // same reference or (null, null) or (DBNull.Value, DBNull.Value)
|
|
return true;
|
|
}
|
|
if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(a, DBNull.Value) ||
|
|
Object.ReferenceEquals(b, null) || Object.ReferenceEquals(b, DBNull.Value))
|
|
{ // (null, non-null) or (null, DBNull.Value) or vice versa
|
|
return false;
|
|
}
|
|
return a.Equals(b);
|
|
}
|
|
|
|
private static bool CompareArray(Array a, Array b)
|
|
{
|
|
if ((null == b) ||
|
|
(1 != a.Rank) ||
|
|
(1 != b.Rank) ||
|
|
(a.Length != b.Length))
|
|
{ // automatically consider array's with Rank>1 not-equal
|
|
return false;
|
|
}
|
|
|
|
int index1 = a.GetLowerBound(0);
|
|
int index2 = b.GetLowerBound(0);
|
|
if (a.GetType() == b.GetType() && (0 == index1) && (0 == index2))
|
|
{
|
|
switch (Type.GetTypeCode(a.GetType().GetElementType()))
|
|
{
|
|
case TypeCode.Byte:
|
|
return DataRowComparer.CompareEquatableArray<Byte>((Byte[])a, (Byte[])b);
|
|
case TypeCode.Int16:
|
|
return DataRowComparer.CompareEquatableArray<Int16>((Int16[])a, (Int16[])b);
|
|
case TypeCode.Int32:
|
|
return DataRowComparer.CompareEquatableArray<Int32>((Int32[])a, (Int32[])b);
|
|
case TypeCode.Int64:
|
|
return DataRowComparer.CompareEquatableArray<Int64>((Int64[])a, (Int64[])b);
|
|
case TypeCode.String:
|
|
return DataRowComparer.CompareEquatableArray<String>((String[])a, (String[])b);
|
|
}
|
|
}
|
|
|
|
//Compare every element. But don't recurse if we have Array of array.
|
|
int length = index1 + a.Length;
|
|
for (; index1 < length; ++index1, ++index2)
|
|
{
|
|
if (!AreElementEqual(a.GetValue(index1), b.GetValue(index2)))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static bool CompareEquatableArray<TElem>(TElem[] a, TElem[] b) where TElem : IEquatable<TElem>
|
|
{
|
|
if (Object.ReferenceEquals(a, b))
|
|
{
|
|
return true;
|
|
}
|
|
if (Object.ReferenceEquals(a, null) ||
|
|
Object.ReferenceEquals(b, null))
|
|
{
|
|
return false;
|
|
}
|
|
if (a.Length != b.Length)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < a.Length; ++i)
|
|
{
|
|
if (!a[i].Equals(b[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This class implements IEqualityComparer using value based semantics
|
|
/// when comparing DataRows.
|
|
/// </summary>
|
|
public sealed class DataRowComparer<TRow> : IEqualityComparer<TRow> where TRow : DataRow
|
|
{
|
|
/// <summary>
|
|
/// Private constructor to prevent initialization outside of Default singleton instance.
|
|
/// </summary>
|
|
private DataRowComparer() { }
|
|
|
|
private static DataRowComparer<TRow> _instance = new DataRowComparer<TRow>();
|
|
|
|
/// <summary>
|
|
/// Gets the singleton instance of the data row comparer.
|
|
/// </summary>
|
|
public static DataRowComparer<TRow> Default { get { return _instance; } }
|
|
|
|
/// <summary>
|
|
/// This method compares to DataRows by doing a column by column value based
|
|
/// comparision.
|
|
/// </summary>
|
|
/// <param name="leftRow">
|
|
/// The first input DataRow
|
|
/// </param>
|
|
/// <param name="rightRow">
|
|
/// The second input DataRow
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if rows are equal, false if not.
|
|
/// </returns>
|
|
public bool Equals(TRow leftRow, TRow rightRow)
|
|
{
|
|
if (Object.ReferenceEquals(leftRow, rightRow))
|
|
{
|
|
return true;
|
|
}
|
|
if (Object.ReferenceEquals(leftRow, null) ||
|
|
Object.ReferenceEquals(rightRow, null))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (leftRow.RowState == DataRowState.Deleted || rightRow.RowState == DataRowState.Deleted)
|
|
{
|
|
throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_CannotCompareDeletedRow);
|
|
}
|
|
|
|
int count = leftRow.Table.Columns.Count;
|
|
if (count != rightRow.Table.Columns.Count)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
if (!DataRowComparer.AreEqual(leftRow[i], rightRow[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This mtheod retrieves a hash code for the source row.
|
|
/// </summary>
|
|
/// <param name="row">
|
|
/// The source DataRow
|
|
/// </param>
|
|
/// <returns>
|
|
/// HashCode for row based on values in the row.
|
|
/// </returns>
|
|
public int GetHashCode(TRow row)
|
|
{
|
|
DataSetUtil.CheckArgumentNull(row, "row");
|
|
|
|
if (row.RowState == DataRowState.Deleted)
|
|
{
|
|
throw DataSetUtil.InvalidOperation(Strings.DataSetLinq_CannotCompareDeletedRow);
|
|
}
|
|
|
|
int hash = 0;
|
|
Debug.Assert(row.Table != null);
|
|
if (row.Table.Columns.Count > 0)
|
|
{
|
|
// if the row has at least one column, then use the first column value
|
|
object value = row[0];
|
|
|
|
Type valueType = value.GetType();
|
|
if (valueType.IsArray)
|
|
{
|
|
Array array = value as Array;
|
|
|
|
if (array.Rank > 1)
|
|
{
|
|
hash = value.GetHashCode();
|
|
}
|
|
else if (array.Length > 0)
|
|
{
|
|
hash = array.GetValue(array.GetLowerBound(0)).GetHashCode();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
System.ValueType vt = value as System.ValueType;
|
|
|
|
// have to unbox value types.
|
|
if (vt != null)
|
|
{
|
|
hash = vt.GetHashCode();
|
|
}
|
|
else
|
|
{
|
|
hash = value.GetHashCode();
|
|
}
|
|
}
|
|
}
|
|
// if table has no columns, the hash code is 0
|
|
return hash;
|
|
}
|
|
}
|
|
}
|