gpt4 book ai didi

c# - 使用 LINQ 将新的 BindingList 与主 BindingList 协调

转载 作者:太空宇宙 更新时间:2023-11-03 20:43:38 25 4
gpt4 key购买 nike

我有一个看似简单的问题,我希望协调两个列表,以便“旧”主列表由包含更新元素的"new"列表更新。元素由键属性表示。这些是我的要求:

  • 仅当任何属性发生更改时,任一列表中具有相同键的所有元素都会导致将"new"列表中的元素分配给“旧”列表中的原始元素。
  • "new"列表中具有不在“旧”列表中的键的任何元素都将添加到“旧”列表中。
  • “旧”列表中具有不在"new"列表中的键的任何元素都将从“旧”列表中删除。

我在这里发现了一个等效问题 - Best algorithm for synchronizing two IList in C# 2.0 - 但它并没有真正得到正确的回答。因此,我想出了一种算法来遍历新旧列表并按照上述进行协调。在有人问我为什么不直接用新列表对象替换旧列表对象之前,这是为了演示目的——这是一个绑定(bind)到 GUI 上的网格的 BindingList,我需要防止刷新伪像,例如闪烁,滚动条移动等。因此列表对象必须保持不变,只有更新后的元素发生变化。

另一件需要注意的事情是,"new"列表中的对象,即使键相同并且所有属性都相同,与“旧”列表中的等效对象是完全不同的实例,因此复制引用不是一个选项。

下面是我到目前为止的想法 - 它是 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 类型方法中的每一个都会产生全新的中间列表(作为返回值),实际上,出于上述所有原因,我只想修改现有列表。

如果有人可以提供帮助,将不胜感激。如果没有,不用担心,上面的方法(可以说是)现在就足够了。

谢谢,杰森

最佳答案

你的主循环是 O(m*n),其中 mn 是旧的大小和新名单。这很糟糕。一个更好的想法可能是首先构建关键元素映射集,然后再处理它们。此外,避免反射是个好主意——可以使用 lambda 作为键选择器。所以:

 public static void Reconcile<T, TKey>(
this BindingList<T> left,
BindingList<T> right,
Func<T, TKey> keySelector)
{
var leftDict = left.ToDictionary(l => keySelector(l));

foreach (var r in right)
{
var key = keySelector(r);
T l;
if (leftDict.TryGetValue(key, out l))
{
// copy properties from r to l
...
leftDict.RemoveKey(key);
}
else
{
left.Add(r);
}
}

foreach (var key in leftDict.Keys)
{
left.RemoveKey(key);
}
}

对于复制属性,我也会避免反射 - 要么为此创建一个类似于 ICloneable 的接口(interface),但用于在对象之间传输属性而不是创建新实例,并拥有所有对象执行它;或者,通过另一个 lambda 将其提供给 Reconcile

关于c# - 使用 LINQ 将新的 BindingList 与主 BindingList 协调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1633320/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com