我创建了一个自定义通用队列,它实现了通用 IQueue 接口,该接口使用 System.Collections.Generic 命名空间中的通用队列作为私有内部队列。示例已清除不相关的代码。
public interface IQueue<TQueueItem>
{
void Enqueue(TQueueItem queueItem);
TQueueItem Dequeue();
}
public class CustomQueue<TQueueItem> : IQueue<TQueueItem>
{
private readonly Queue<TQueueItem> queue = new Queue<TQueueItem>();
...
public void Enqueue(TQueueItem queueItem)
{
...
queue.Enqueue( queueItem );
...
}
public TQueueItem Dequeue()
{
...
return queue.Dequeue();
...
}
}
我想让事情与核心实现保持一致,并且注意到核心队列实现了 IEnumerable,因此我将通过在类上显式实现 IEnumerable 或使用 IQueue 接口继承它来执行相同的操作。
我想知道的是,在枚举队列时,每个移动下一个项目是否都应该使下一个项目出队?我已经使用 Reflector 来了解 Microsoft 是如何做到这一点的,他们所做的只是单步执行队列私有数组,但 Microsoft 远非绝对可靠,因此我想获得一般意见。
public class CustomQueue<TQueueItem> : IQueue<TQueueItem>, IEnumerable<TQueueItem>
{
...
public IEnumerator<TQueueItem> GetEnumerator()
{
while (queue.Count > 0)
{
yield return Dequeue();
}
}
//Or
public IEnumerator<TQueueItem> GetEnumerator()
{
return queue.GetEnumerator();
}
...
}
我有两种想法,一方面,我觉得迭代集合不应该改变集合状态,但另一方面,特别是对于我的特定实现,它会让用法看起来干净。
EDIT
将事情放在上下文中。我正在实现的类在出队时执行 Monitor.Wait 并且队列中没有项目。当一个项目被放入队列时,就会有一个 Monitor.Pulse。这允许一个线程将内容推送到队列中,而另一个线程本质上“监视”队列。
从编码的角度来看,我试图决定哪个看起来更干净:
foreach(QueueItem item in queue)
{
DoSomethingWithThe(item);
}
//Or
while(systemIsRunning)
{
DoSomethingWithThe(queue.Dequeue());
}
对于我的特定实现,如果有多个进程出队项目并不重要。因为它是一个队列,所以它们都可以选择一个项目,因为任何项目都不应被处理多次,因此使用队列。
EDIT
有趣的是,我发现一篇博客文章中有人已经做到了这一点。
Link https://learn.microsoft.com/en-us/archive/blogs/toub/blocking-queues
EDIT
在我结束这篇文章之前,最后一次尝试。人们对于该类没有实现 IEnumerable 但具有使项目出队的 IEnumerator GetEnumerator() 方法有何感受? .net 语言支持鸭子类型,foreach 就是其中之一。也许这值得它自己提出问题?
EDIT
提出了实现 GetEnumerator 方法而不在另一个方法中实现 IEnumerable 的问题question https://stackoverflow.com/questions/4194900/should-a-getenumerator-method-still-be-idempotent-when-the-class-does-not-implem.