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

C# ExcelAsyncUtil.Observe-在Excel中创建运行时钟,c#,excel,asynchronous,excel-dna,C#,Excel,Asynchronous,Excel Dna,我正在尝试ExcelAsyncUtil.Observe函数。我编写了以下代码,在Excel中显示一个正在运行的时钟。它工作得很好,但我不确定我在做什么。两个问题: 我应该为observer.OnCompleted()和observer.OnError()添加功能吗?这些电话有什么作用 在IDisposable类中我应该做什么?为什么在那里 以下是我的示例代码: [ExcelFunction] public static object MyExcelTicker() {

我正在尝试ExcelAsyncUtil.Observe函数。我编写了以下代码,在Excel中显示一个正在运行的时钟。它工作得很好,但我不确定我在做什么。两个问题:

  • 我应该为observer.OnCompleted()和observer.OnError()添加功能吗?这些电话有什么作用
  • 在IDisposable类中我应该做什么?为什么在那里
  • 以下是我的示例代码:

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

    IExcelObserver接口与反应式扩展库()中IObserver接口的语义匹配

    函数可以调用OnNext零次或多次,然后在发生错误时调用OnError,或者在不再引发其他事件时调用OnCompleted。Excel DNA将像处理常规UDF引发的异常一样处理OneError,并将#值返回到单元格或通过已注册的未处理ExceptionHandler处理异常。OnCompleted在Excel上下文中不是很有用-它只是表示不会再提出其他值

    例如,错误似乎不是问题,事件流没有尽头,因此您永远不需要调用OnError或OnCompleted


    当可观察对象不再连接到细胞公式时,Excel DNA基础结构将调用IDisposable.Dispose。例如,如果从单元格中删除带有MyExcelTicker()调用的公式。您可以将此作为一个通知来清理任何后端资源,如果您不感兴趣,也可以忽略该通知。

    已经有一段时间了,至少还有一件事情没有涉及,所以让我补充一下Govert所说的

    你问过:

    public class TickerDisposable : IDisposable
    {
        public void Dispose()
        {
            // What to do here?
        }
    }
    
    让我们总结一下:

    对于时钟滴答器的每个新订户,将在TickerObservable上调用Subscribe。因此,对于每个订户,您的代码将创建一个新的
    System.Timers.Timer
    和一个新的
    Timer.appeased
    事件处理程序,以获得预期效果。实际上,这就是你想要达到效果所需要的一切

    但是,您还需要返回IDisposable,因此您仅为此目的创建了一个虚拟TickerDisposable,您不确定它的用途

    答复:

    库要求您从订阅中返回的IDisposable仅允许您在闪烁停止后进行清理。计时器是一个“系统的东西”。一旦创建并启动它们,它们就会运行。一个小时后,它们不能被GC'ed,因为它们注定要运行,直到你停止它们。当然,您已经+='ed了一个事件处理程序,观察者(如果弱引用)可能已经死了,但是您的计时器不知道!你必须在某个时候停止它

    因此,IDisposable相关模式借鉴了RX:无论您在Subscribe方法中分配、保留、构建什么,都要在(您的!)IDisposable中添加一些注意事项。然后,当观察者取消订阅时,您的IDisposable也将被清理,并且您的自定义Dispose方法将被运行,该方法将能够查看IDisposable的内容和。。清理垃圾,或者更确切地说,解锁它,以便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;
        }
    }
    
    现在,您完全可以肯定,没有死掉的处理程序会在长寿的共享计时器前徘徊。(当然,这里没有清理计时器,但这是另一回事。)。也许你已经有了这个想法,所以,玩得开心