RemoveAll是否适用于ObservableCollections?


Answers:


99

我不知道仅删除所选项目的方法。但是创建扩展方法很简单:

public static class ExtensionMethods
{
    public static int Remove<T>(
        this ObservableCollection<T> coll, Func<T, bool> condition)
    {
        var itemsToRemove = coll.Where(condition).ToList();

        foreach (var itemToRemove in itemsToRemove)
        {
            coll.Remove(itemToRemove);
        }

        return itemsToRemove.Count;
    }
}

这将从ObservableCollection符合条件的所有项目中删除。您可以这样称呼它:

var c = new ObservableCollection<SelectableItem>();
c.Remove(x => x.IsSelected);

在return语句之前是否需要添加itemsToRemove.Clear()?
JoanComasFdz 2012年

@JoanComasFdz:itemstoRemove不会,垃圾收集器会声明。
丹尼尔·希尔加斯

1
@ ShaktiPrakashSingh:好点。返回一个集合表示原始集合未更改,并且已创建一个新集合。如您所指出的,事实并非如此。此外,on的方法List<T>返回计数,因此遵循该方法将是一个好主意。我相应地更改了答案。感谢您的意见。
Daniel Hilgarth

@DanielHilgarth非常感谢,您度过了我美好的一天!
Nicolas Bodin-Ripert

1
@DanielHilgarth大声笑,这是公司代码库内部的扩展。已删除评论。
安德里亚斯

47

向后迭代比在Daniel Hilgarth的示例中创建临时集合要有效。

public static class ObservableCollectionExtensions
{
    public static void RemoveAll<T>(this ObservableCollection<T> collection,
                                                       Func<T, bool> condition)
    {
        for (int i = collection.Count - 1; i >= 0; i--)
        {
            if (condition(collection[i]))
            {
                collection.RemoveAt(i);
            }
        }
    }
}

2
在测试中的真实应用程序中,我发现这确实比temp收集要快-平均时间为44毫秒,峰值为62毫秒,而与temp收集为平均时间为55
毫秒

14

一线执行的效果如何?

observableCollection.Where(l => l.type == invalid).ToList().All(i => observableCollection.Remove(i))

-编辑-

抱歉,是的,您需要一个中间的ToList()来强制前一半进行评估,因为LINQ默认情况下会进行惰性评估。


我尝试了此操作,但最终出现错误:“集合已修改;枚举操作可能无法执行。”
有用蜜蜂

我发现这是一个非常简单/优雅的解决方案。在投票数更多的较长解决方案中,这是否有任何弊端?
Shimeon '16

可读性,也许。第一次阅读时尚不清楚,但是接受的答案将其分为两个明显的部分。
simonalexander2005

我建议List<T>.ForEach不要使用All。我认为这All(...)是之前ToList()添加的人工产物。
grek40

10

此处提出的使用例程逐项删除项目的每种解决方案都有一个故障。想象一下,您有许多可观察的项目,假设有10.000个项目。然后,您要删除满足某些条件的项目。

如果您使用Daniel Hilgarth的解决方案并致电:c.Remove(x => x.IsSelected);并且例如有3000项要删除的项目,则建议的解决方案将通知每次删除的项目。这是由于内部实现会Remove(item)通知有关更改的事实。这将在删除过程中针对3000个项目中的每一个进行调用。

所以代替这个,我创建了ObservableCollection的后代并添加了新方法 RemoveAll(predicate)

[Serializable]
public class ObservableCollectionExt<T> : ObservableCollection<T>
{
    public void RemoveAll(Predicate<T> predicate)
    {
        CheckReentrancy();

        List<T> itemsToRemove = Items.Where(x => predicate(x)).ToList();
        itemsToRemove.ForEach(item => Items.Remove(item));

        OnPropertyChanged(new PropertyChangedEventArgs("Count"));
        OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}

有趣的是itemsToRemove.ForEach(item => Items.Remove(item));。直接致电Items.Remove(item)不会通知您已删除商品。

取而代之的是,在删除必需的项目之后,通过调用立即通知更改:

OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

WPF注意:与ItemsControl一起使用时,使用NotifyCollectionChangedAction.Reset时,控件将在内部调用Clear;这导致不良副作用在我的具体使用情况,当我对新添加的项目动画〜referencesource.microsoft.com/PresentationFramework/R/...
cookbr

2
在测试中的真实应用程序中,我发现它实际上比temp收集慢-平均时间为67ms,峰值为152ms,而temp收集为平均时间为55ms,峰值为93ms。请记住,这只是一个小集合。
danio '16

1

无法像普通列表一样将表达式传递给ObservableCollection来删除匹配项。ObservableCollection一次添加和删除一项。

为此,您必须创建自己的INotifyCollectionChanged实现,或者如您提到的那样创建扩展方法。


1

这是我的扩展方法解决方案的版本,仅对可接受的答案稍有变化,但具有的优势是,返回的计数基于已确认从集合中移除该项目:

public static class ObservableCollectionExtensionMethods
{
    /// <summary>
    /// Extends ObservableCollection adding a RemoveAll method to remove elements based on a boolean condition function
    /// </summary>
    /// <typeparam name="T">The type contained by the collection</typeparam>
    /// <param name="observableCollection">The ObservableCollection</param>
    /// <param name="condition">A function that evaluates to true for elements that should be removed</param>
    /// <returns>The number of elements removed</returns>
    public static int RemoveAll<T>(this ObservableCollection<T> observableCollection, Func<T, bool> condition)
    {
        // Find all elements satisfying the condition, i.e. that will be removed
        var toRemove = observableCollection
            .Where(condition)
            .ToList();

        // Remove the elements from the original collection, using the Count method to iterate through the list, 
        // incrementing the count whenever there's a successful removal
        return toRemove.Count(observableCollection.Remove);
    }
}

1
ObservableCollection<AppVariable<G>> _appVariables = new new ObservableCollection<AppVariable<G>>();

var temp = AppRepository.AppVariables.Where(i => i.IsChecked == true).OrderByDescending(k=>k.Index);

foreach (var i in temp)
{
     AppRepository.AppVariables.RemoveAt(i.Index);
}

1
请尝试添加一些简短的答案。
Farhana
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.