ExcelAsyncUtil.Observe - 在Excel中创建运行时钟

时间:2013-08-02 22:23:25

标签: c# excel asynchronous excel-dna

我正在尝试ExcelAsyncUtil.Observe功能。我做了以下代码,在Excel中显示了一个运行时钟。它工作正常,但我不知道我在做什么。两个问题:

  1. 我应该为observer.OnCompleted()和observer.OnError()添加功能吗?这些电话有什么作用?
  2. 我应该在IDisposible课程中做些什么?为什么会这样?
  3. 以下是我的示例代码:

        [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?
            }
        }
    

2 个答案:

答案 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()调用的公式。您可以将此作为通知来清理任何后端资源,或者如果您不感兴趣则忽略该通知。