我正在尝试ExcelAsyncUtil.Observe功能。我做了以下代码,在Excel中显示了一个运行时钟。它工作正常,但我不知道我在做什么。两个问题:
以下是我的示例代码:
[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?
}
}
答案 0 :(得分:4)
已经有一段时间了,至少有一件事还没有被覆盖,所以让我补充一下Govert说的话。
你问过:
public class TickerDisposable : IDisposable
{
public void Dispose()
{
// What to do here?
}
}
让我们总结一下:
对于时钟自动收报机的每个新订户,将在TickerObservable上调用订阅。因此,对于每个订阅者,您的代码将创建一个新的System.Timers.Timer
和一个新的timer.Elapsed
事件处理程序 - 以获得预期的效果。实际上,你需要达到你的效果。
但是,您还需要返回一个IDisposable,因此您只为此目的创建了一个虚拟TickerDisposable,并且您不确定它的用途。
答案:
图书馆要求您从订阅中返回的IDisposable只是为了让您在闪光停止闪光后进行清理。定时器是一个“系统事物”。创建并启动它们后,它们运行。一个小时后,他们无法进行GC,因为他们意味着运行,直到你停止它们为止。当然,你已经+ ='一个事件处理者,观察者(如果弱引用)可能已经死了,但你的计时器不知道!你必须在某个时候停止它。
因此,从RX借来的IDisposable相关模式:无论你在Subscribe方法中分配,保留,构建等重度或长寿,都要注意它(你的!)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;
}
}
现在你完全确定没有死去的人会在长寿共享计时器上挥之不去。 (当然,这里缺少计时器的清理,但这是另一回事......)。可能你已经有了这个想法,所以,玩得开心!
答案 1 :(得分:1)
IExcelObserver 接口匹配来自Reactive Extensions库(http://msdn.microsoft.com/en-us/library/dd783449.aspx)的IObserver接口的语义。
您的功能可以调用 OnNext 零次或多次,然后在发生错误时调用 OnError ,或者如果没有其他事件将 OnCompleted 被提高。 Excel-DNA将处理OnError,因为它将是常规UDF抛出的异常,并将返回#VALUE到单元格或通过已注册的UnhandledExceptionHandler处理异常。 OnCompleted在Excel上下文中没那么有用 - 它只是表明不会引发其他值。
对于您的示例,错误似乎不是问题,并且事件流没有结束,因此您永远不需要调用OnError或OnCompleted。
当observable不再连接到单元格公式时,Excel-DNA基础结构将调用 IDisposable.Dispose 。例如,如果从单元格中删除了带有MyExcelTicker()调用的公式。您可以将此作为通知来清理任何后端资源,或者如果您不感兴趣则忽略该通知。