简体   繁体   English

如何在过滤时将项目添加到集合?

[英]How to add items to a collection, while filtering it?

I have a collection of log events. 我有一些日志事件。 I give the user the ability to filter out different log levels. 我为用户提供了过滤不同日志级别的能力。 During that filtering process, if a new log event is added, an exception is thrown. 在该筛选过程中,如果添加了新的日志事件,则会引发异常。 I can just lock the collection while filtering it, however this means that any new log events won't be added in the correct order, or at all(?). 我可以在过滤集合时锁定它,但是这意味着将不会以正确的顺序或根本不会添加任何新的日志事件。

The filtering method: 过滤方法:

//Filters and returns a list of filtered log events
    private IEnumerable<LogEvent> FilterLogEvents()
    {
        return (from x in LogEvents
                where ((ViewDebugLogs == true) ? x.Level == "Debug" : false)
                || ((ViewErrorLogs == true) ? x.Level == "Error" : false)
                || ((ViewInfoLogs == true) ? x.Level == "Info" : false)
                select x);
    }

If I just return a list, the same thing happens if it's in mid-query. 如果我只返回一个列表,那么在中间查询中也会发生同样的事情。

How do I go about adding to a collection, while at the same time using it? 如何在添加到集合的同时使用它? The main concern here is making sure log events DO get added in the proper order to the collection if it is added mid-filter. 如果将日志事件添加到中间过滤器中,则主要要确保将日志事件按正确的顺序添加到集合中。 So if I cannot add the log events at the same time, can I somehow queue them up and insert them correctly afterwards? 因此,如果我不能同时添加日志事件,是否可以以某种方式将它们排队并在以后正确插入它们?

No, a collection can't be modified when being enumerated. 不可以,枚举时不能修改集合。 You can copy the collection, enumerate the copy, and modify the original collection during the enumeration. 您可以在枚举期间复制集合,枚举副本并修改原始集合。

//the following example throws an exception
var numbers = GetTheNumbers();
foreach(var number in numbers)
{
    numbers.Add(1);   //exception thrown
}

//copy the collection first
var numbers = GetTheNumbers();
var copy = numbers.ToArray();
foreach(var number in copy)
{
    numbers.Add(1);  //safe
}

You can iterate over some of the concurrent collections while adding items to them. 您可以在将一些并发集合添加到项目时对其进行迭代。 One such collection is ConcurrentQueue<T> , which I think might be suitable for a log if the following conditions are true: 这样的集合之一就是ConcurrentQueue<T> ,如果满足以下条件,我认为它可能适合于日志:

  • You only want to add items at the end of the log. 您只想在日志末尾添加项目。
  • You never need to empty it in one go. 您无需一口气将其清空。 (Rather than emptying a queue, you'd have to create a new empty one, or you'd have to loop calling TryDequeue() until it was empty). (而不是清空队列,您必须创建一个新的空队列,或者必须循环调用TryDequeue()直到它为空)。

Note that when you iterate over a ConcurrentQueue<T> it appears that the count is read at the start of the iteration and not updated during the iteration, so if new items are added while you're iterating you won't see them (but the addition of the items won't cause an exception). 请注意,当您遍历ConcurrentQueue<T> ,似乎在迭代开始时读取了计数,并且在迭代过程中未更新计数,因此,如果在迭代时添加了新项目,您将看不到它们(但是项目的添加不会引起异常)。

Here's a demo program: 这是一个演示程序:

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace Demo
{
    internal class Program
    {
        public static void Main()
        {
            var queue = new ConcurrentQueue<int>();

            Task.Run(() =>
            {
                for (int i = 0; i < 1000; ++i)
                {
                    queue.Enqueue(i);
                    Thread.Sleep(1);
                }
            });

            Thread.Sleep(100);
            Console.WriteLine($"Count before loop: {queue.Count}");

            foreach (var i in queue)
                Console.WriteLine($"{i}, n={queue.Count}");
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM