【发布时间】:2010-12-10 15:19:00
【问题描述】:
我有一个看似简单的问题,我希望协调两个列表,以便“旧”主列表由包含更新元素的“新”列表更新。元素由键属性表示。这些是我的要求:
- 只有在任何属性发生更改时,任一列表中具有相同键的所有元素都会将“新”列表中的该元素分配给“旧”列表中的原始元素。
- “新”列表中具有不在“旧”列表中的键的任何元素都将被添加到“旧”列表中。
- “旧”列表中具有不在“新”列表中的键的任何元素都将从“旧”列表中删除。
我在这里发现了一个类似的问题 - Best algorithm for synchronizing two IList in C# 2.0 - 但它并没有真正得到正确的回答。所以,我想出了一个算法来遍历旧列表和新列表,并按照上述执行协调。在有人问我为什么不只是用新列表对象完全替换旧列表对象之前,它是出于演示目的 - 这是一个绑定到 GUI 上的网格的 BindingList,我需要防止刷新工件,例如闪烁,滚动条移动等。所以列表对象必须保持不变,只有更新的元素发生了变化。
另外需要注意的是,“新”列表中的对象,即使键相同且所有属性都相同,与“旧”列表中的等效对象是完全不同的实例,因此复制参考不是一种选择。
以下是我目前所提出的 - 它是 BindingList 的通用扩展方法。我已经放入 cmets 来展示我正在尝试做的事情。
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 类型方法中的每一个都会产生全新的中间列表(作为返回值),实际上,出于上述所有原因,我只想修改现有列表。
如果有人能提供帮助,将不胜感激。如果没有,不用担心,上述方法(实际上)现在就足够了。
谢谢, 杰森
【问题讨论】:
-
相关;这可能有用:groups.google.com/group/…