比较器
.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
实现是愚蠢的而且相当无用,但它有效;)。阅读此线程以了解如何正确实现它。阅读此线程以了解它的使用位置以及在您的情况下实施它是否如此重要。
2) 我确实检查了 的值null
,因为我讨厌NullReferenceException
s。但是,这里可能不是这种情况,因为 null 值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);
}