У меня, казалось бы, простая проблема, когда я хочу согласовать два списка, чтобы «старый» основной список обновлялся «новым» списком, содержащим обновленные элементы. Элементы обозначаются ключевым свойством. Это мои требования:
- Все элементы в любом списке, имеющие одинаковый ключ, приводят к назначению этого элемента из «нового» списка поверх исходного элемента в «старом» списке, только если какие-либо свойства были изменены.
- Любые элементы в «новом» списке, ключи которых отсутствуют в «старом» списке, будут добавлены в «старый» список.
- Все элементы в «старом» списке, ключи которых отсутствуют в «новом» списке, будут удалены из «старого» списка.
Я нашел здесь эквивалентную проблему - Лучший алгоритм для синхронизации двух IList в C # 2.0 - но на самом деле он не получил правильного ответа. Итак, я придумал алгоритм для перебора старого и нового списков и выполнения сверки в соответствии с вышеизложенным. Прежде чем кто-либо спросит, почему я не просто заменяю старый объект списка новым объектом списка целиком, он предназначен для целей презентации - это BindingList, привязанный к сетке в графическом интерфейсе, и мне нужно предотвратить такие артефакты обновления, как мигание, перемещение полос прокрутки и т. д. Таким образом, объект списка должен оставаться прежним, изменялись только его обновленные элементы.
Еще одна вещь, которую следует отметить, это то, что объекты в «новом» списке, даже если ключ одинаков и все свойства одинаковы, являются абсолютно разными экземплярами для эквивалентных объектов в «старом» списке, поэтому копирование ссылки не вариант.
Ниже приведено то, что я до сих пор придумал, - это общий метод расширения для BindingList. Я добавил комментарии, чтобы продемонстрировать, что я пытаюсь сделать.
public static class BindingListExtension
{
public static void Reconcile<T>(this BindingList<T> left,
BindingList<T> right,
string key)
{
PropertyInfo piKey = typeof(T).GetProperty(key);
// Go through each item in the new list in order to find all updated and new elements
foreach (T newObj in right)
{
// First, find an object in the new list that shares its key with an object in the old list
T oldObj = left.First(call => piKey.GetValue(call, null).Equals(piKey.GetValue(newObj, null)));
if (oldObj != null)
{
// An object in each list was found with the same key, so now check to see if any properties have changed and
// if any have, then assign the object from the new list over the top of the equivalent element in the old list
foreach (PropertyInfo pi in typeof(T).GetProperties())
{
if (!pi.GetValue(oldObj, null).Equals(pi.GetValue(newObj, null)))
{
left[left.IndexOf(oldObj)] = newObj;
break;
}
}
}
else
{
// The object in the new list is brand new (has a new key), so add it to the old list
left.Add(newObj);
}
}
// Now, go through each item in the old list to find all elements with keys no longer in the new list
foreach (T oldObj in left)
{
// Look for an element in the new list with a key matching an element in the old list
if (right.First(call => piKey.GetValue(call, null).Equals(piKey.GetValue(oldObj, null))) == null)
{
// A matching element cannot be found in the new list, so remove the item from the old list
left.Remove(oldObj);
}
}
}
}
Это можно назвать так:
_oldBindingList.Reconcile(newBindingList, "MyKey")
Однако я, возможно, ищу способ сделать то же самое, используя методы типа LINQ, такие как GroupJoin <>, Join <>, Select <>, SelectMany <>, Intersect <> и т. Д. Пока проблема У меня было то, что каждый из этих методов типа LINQ приводит к совершенно новым промежуточным спискам (в качестве возвращаемого значения), и на самом деле, я только хочу изменить существующий список по всем вышеуказанным причинам.
Если кто-нибудь может помочь с этим, был бы очень признателен. Если нет, то не беспокойтесь, вышеописанного метода (как бы) пока хватит.
Спасибо,
Jason * +1027 *