ExcelAsyncUtil.Observe – 在Excel中创build运行时钟
我正在尝试ExcelAsyncUtil.Observe函数。 我做了下面的代码,显示在Excel中运行的时钟。 它工作正常,但我不知道我在做什么。 两个问题:
- 我应该为observer.OnCompleted()和observer.OnError()添加function吗? 这些电话是做什么的?
- 我应该做什么在IDisposible类? 为什么呢?
这是我的示例代码:
[ExcelFunction] public static object MyExcelTicker() { return ExcelAsyncUtil.Observe("MyExcelTicker", new object[] { }, TickerFunction()); } public static ExcelObservableSource TickerFunction() { ExcelObservableSource source = new ExcelObservableSource(() => new TickerObservable()); return source; } public class TickerObservable : IExcelObservable { public IDisposable Subscribe(IExcelObserver observer) { var timer = new System.Timers.Timer(); timer.Interval = 1000; timer.Elapsed += (s, e) => observer.OnNext(DateTime.Now.ToString()); timer.Start(); // What about observer.OnCompleted() and observer.OnError()? return new TickerDisposable(); } } public class TickerDisposable : IDisposable { public void Dispose() { // What to do here? } }
已经有一段时间了,至less还有一件东西还没有被覆盖,所以让我补充Govert所说的话。
你问过:
public class TickerDisposable : IDisposable { public void Dispose() { // What to do here? } }
我们总结一下:
对于您的时钟机的每个新订阅者,订阅将在TickerObservable上被调用。 因此,对于每个订户,您的代码将创build一个新的System.Timers.Timer
和一个新的timer.Elapsed
事件处理程序 – 以获得您的预期效果。 而这其实就是你需要得到你的效果的全部。
但是,您还需要返回一个IDisposable,因此您已经创build了一个虚拟TickerDisposable专为此目的,而您不知道它是什么。
回答:
图书馆要求你从订阅中返回的IDisposable仅仅是为了让你在你的闪光停止闪耀后清理。 定时器是一个“系统事物”。 一旦你创build它们并启动它们,它们就会运行 。 一小时之后,他们就不能开始GC了,因为他们是要跑到你停下来的。 当然,你已经logging了一个事件,观察者(如果弱引用)可能已经死了,但你的计时器不知道! 你必须在某个时候停止它。
因此,从RX中借用的IDisposable相关模式:无论您在Subscribe方法中分配,预留,构build等等的重或长期生活,都将其注释到(您的!)IDisposable中。 然后,当观察者取消订阅,你的IDisposable也将被清理,并且你的自定义Dispose方法将被运行,这将能够看你的IDiposable的内容,并清理垃圾,或者说, 解开它,所以GC可以冲洗他们。
完成你的例子:
public class TickerObservable : IExcelObservable { public IDisposable Subscribe(IExcelObserver observer) { var timer = new System.Timers.Timer(); timer.Interval = 1000; timer.Elapsed += (s, e) => observer.OnNext(DateTime.Now.ToString()); timer.Start(); return new TickerDisposable(timer); } } public class TickerDisposable : IDisposable { private Timer ticky; public TickerDisposable(Timer timer) { ticky = timer; } public void Dispose() { if(ticky != null) ticky.Dispose(); // or Stop, or etc.. } }
上面的例子实际上是返回的IDisposable的最明显的用法。 但是,您可以将其用于任何注册 – 取消注册通知。 例如,对于单个共享计时器,它可能看起来像这样:
public class TickerObservable : IExcelObservable { private static Timer timer = ..... ; // assume it is up & running & shared public IDisposable Subscribe(IExcelObserver observer) { ElapsedEventHander hd = (s, e) => observer.OnNext(DateTime.Now.ToString()); timer.Elapsed += hd; return new TickerDisposable(timer, hd); } } public class TickerDisposable : IDisposable { private Timer ticky; private ElapsedEventHander handler; public TickerDisposable(Timer timer, ElapsedEventHander hd) { ticky = timer; handler = hd; } public void Dispose() { if(ticky != null && handler != null) ticky.Elapsed -= handler; } }
而现在你完全确定,没有任何死刑犯在长期生活共享计时器上挥之不去。 (当然,定时器的清理在这里是不存在的,但这是另一回事..)。 也许你已经有了这个想法,所以,玩得开心!
IExcelObserver接口与Reactive Extensions库( http://msdn.microsoft.com/zh-cn/library/dd783449.aspx )中的IObserver接口的语义相匹配。
您的函数可以调用OnNext零次或多次,如果发生错误,则调用OnError;如果没有其他事件发生,则OnCompleted 。 Excel-DNA将处理OnError,因为它会引起常规UDF引发的exception,并将#VALUE返回给单元格,或通过注册的UnhandledExceptionHandler处理exception。 OnCompleted在Excel上下文中并不那么有用 – 它只是表示不会再引发更多的值。
对于你的例子,错误似乎不是一个问题,并没有结束的事件stream,所以你不需要调用OnError或OnCompleted。
当可观察元素不再连接到单元格公式时,Excel-DNA基础结构将调用IDisposable.Dispose 。 例如,如果带有MyExcelTicker()调用的公式从单元格中删除。 您可以将其用作通知来清理所有后端资源,或者在不感兴趣的情况下忽略通知。