Comparer
.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
с. Однако это может быть не так, потому что нулевые значения в 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);
}