【问题标题】:C#: Triggering an Event when an object is added to a QueueC#:将对象添加到队列时触发事件
【发布时间】:2010-10-06 14:19:02
【问题描述】:

我需要能够在将对象添加到Queue<Delegate> 时触发事件。

我创建了一个扩展 Queue 的新类:

public delegate void ChangedEventHandler(object sender, EventArgs e);

public class QueueWithChange<Delegate> : Queue<Delegate>
{
    public event ChangedEventHandler Changed;

    protected virtual void OnChanged(EventArgs e) {
        if (Changed != null)
        {
            Changed(this, e);
        }
    }
}

然后从另一个类附加事件,如下所示:

QueueWithChange<TimerDelegate> eventQueue = new QueueWithChange<TimerDelegate>();

//

eventQueue.Changed += new ChangedEventHandler(delegate(object s, EventArgs ex) {
    //This event is not being triggered, so this code is unreachable atm...and that is my problem

    if (eventQueue.Count > 0)
    {
        eventQueue.Dequeue().Invoke(new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(5) });
        actionTimer.Stop();
    }
});

但每当我将一个对象 (eventQueue.Enqueue(something)) 加入队列时,附加的事件都不会被触发。

我在这里错过了什么?

【问题讨论】:

    标签: c# events delegates queue


    【解决方案1】:

    如果您指的是非泛型 Queue 类,那么您可以直接覆盖 Enqueue

    public override void Enqueue(object obj)
    {
        base.Enqueue(obj);
        OnChanged(EventArgs.Empty);
    }
    

    但是,如果您指的是泛型 Queue&lt;T&gt; 类,请注意没有合适的虚拟方法可以覆盖。您最好用自己的类封装队列:

    (** 重要编辑:删除基类!!!**)

    class Foo<T>
    {
        private readonly Queue<T> queue = new Queue<T>();
        public event EventHandler Changed;
        protected virtual void OnChanged()
        {
            if (Changed != null) Changed(this, EventArgs.Empty);
        }
        public virtual void Enqueue(T item)
        {
            queue.Enqueue(item);
            OnChanged();
        }
        public int Count { get { return queue.Count; } }
    
        public virtual T Dequeue()
        {
            T item = queue.Dequeue();
            OnChanged();
            return item;        
        }
    }
    

    但是,查看您的代码,您似乎可能在这里使用了多个线程。如果是这种情况,请考虑使用threaded queue

    【讨论】:

    • 优秀;这正是我想要的。感谢马克的高质量回答。我还将检查您提到的线程队列。
    • 从设计原则的角度来看,最好使用组合而不是继承(从广义上讲),所以我支持你的解决方案。
    • Marc,你删除基类的原因是什么?
    • 这是一个错误!我打算使用组合,而不是继承——因为我们已经观察到继承不能提供我们需要的东西......
    • @adopilot while(Count&gt;0) yield return Dequeue();
    【解决方案2】:

    我刚刚写了我所谓的 TriggeredQueue。它启发了 Marc Gravell 的答案。

    你可以在这里找到我的帖子:http://joesauve.com/triggeredqueuet

    这里的要点:http://gist.github.com/jsauve/b2e8496172fdabd370c4

    它有四个事件:

    • 将入队
    • 将出队
    • 入队
    • DidDequeue

    您可以像这样挂接其中任何一个:

    YourQueue.WillEnqueue += (sender, e) => {
        // kick off some process
    };
    YourQueue.DidEnqueue += (sender, e) => {
        // kick off some process
        // e.Item provides access to the enqueued item, if you like
    };
    YourQueue.WillDequeue += (sender, e) => {
        // kick off some process
    };
    YourQueue.DidDequeue += (sender, e) => {
        // kick off some process
        // e.Item provides access to the dequeued item, if you like
    };
    

    一个巧妙的技巧是,您可以使用 DidDequeue 方法启动一些进程,以通过发出 Web 请求或从文件系统加载一些数据等来确保队列已满。我在 Xamarin 移动应用程序中使用这个类来确保预先缓存数据和图像以提供流畅的用户体验,而不是在图像滚动到屏幕后加载图像(就像您在 Facebook 和无数其他应用中看到的那样)。

    【讨论】:

      【解决方案3】:

      试试

      public new void Enqueue(Delegate d)
      {
          base.Enqueue(d);
          OnChanged(EventArgs.Empty);
      }
      

      【讨论】:

      • 重新声明/隐藏方法是脆的、非多态的、脆的;调用者可以简单地跳过你的代码。
      【解决方案4】:

      您必须覆盖 Enqueue,才能调用 OnChanged。

      【讨论】:

      • Enqueue 在通用队列上不是虚拟的
      • 对,真可惜。我想你必须创建一个 IQueue 接口并拥有自己的实现。
      猜你喜欢
      • 2020-06-02
      • 1970-01-01
      • 2012-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多