比较器
.net BCL 包含可用于声明满足您需求的自定义比较的抽象:
class RowDataComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow x, DataRow y)
{
// add check for nulls, different ItemArray length, whatever
var xv = x.ItemArray;
var yv = y.ItemArray;
for (int i = 0; i < xv.Length; i++)
if(xv[i] == null && yv[i] == null)
continue;
else if (xv[i]==null || !xv[i].Equals(yv[i]))
return false;
return true;
}
public int GetHashCode(DataRow obj)
=> obj[0]?.GetHashCode() ?? 0;
}
这个比较器中你需要知道的一些有趣的事情:
1) GetHashCode 的实现是愚蠢的而且相当无用,但它确实有效;)。阅读this thread 了解如何正确实现它。阅读this thread,了解它的用途以及在您的情况下实施它是否如此重要。
2) 我确实检查了null 的值,因为我讨厌NullReferenceExceptions。但是,这里可能不是这样,因为DataRow 中的空值被DBNull.Value 替换
3) 我不使用运算符== 来比较对象。它可能适用于实现Equals/GetHashCode 的引用类型,否则它将比较引用。装箱的值总是有不同的引用,所以如果你有例如整数列,== 将不起作用。试试这个来感受不同:Console.WriteLine((object)42 == (object)42) 和 Console.WriteLine(((object)42).Equals((object)42))
差异
假设架构是相同的,键是整数,并且您不关心额外/缺失的行,您可以使用如下所示:
class RowsComparer
{
Dictionary<int, DataRow> _leftRows;
Dictionary<int, DataRow> _rightRows;
HashSet<int> _commonKeys;
private static Dictionary<int, DataRow> toRows(DataTable table)
=> table.Rows.OfType<DataRow>().ToDictionary(r => (int)r.ItemArray[0]);
public RowsComparer(DataTable left, DataTable right)
{
_leftRows = toRows(left);
_rightRows = toRows(right);
_commonKeys = new HashSet<int>(_leftRows.Keys.Intersect(_rightRows.Keys));
}
public IEnumerable<DataRow> Diffs()
=> _commonKeys.Select(k => _rightRows[k]).Except(_leftRows.Values, new RowDataComparer());
public IEnumerable<DataRow> Extra()
=> _leftRows.Where(kv => !_commonKeys.Contains(kv.Key)).Select(kv => kv.Value);
public IEnumerable<DataRow> Missing()
=> _rightRows.Where(kv => !_commonKeys.Contains(kv.Key)).Select(kv => kv.Value);
}