2

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

  1. 我应该为observer.OnCompleted() 和observer.OnError() 添加功能吗?这些调用有什么作用?
  2. 我应该在 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?
        }
    }
4

2 回答 2

4

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

你问过:

public class TickerDisposable : IDisposable
{
    public void Dispose()
    {
        // What to do here?
    }
}

让我们总结一下:

对于您的时钟记录器的每个新订阅者,将在 TickerObservable 上调用订阅。因此,对于每个订阅者,您的代码将创建一个新的System.Timers.Timer和一个新的timer.Elapsed事件处理程序 - 以获得您想要的效果。这实际上就是获得效果所需的全部内容。

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

回答:

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

因此,从 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;
    }
}

现在您完全可以确定没有死处理程序在长寿命共享计时器上徘徊。(当然这里缺少计时器的清理,但这是另一回事..)。可能您已经有了这个想法,所以,玩得开心!

于 2013-09-11T15:37:45.367 回答
1

IExcelObserver接口与反应式扩展库 ( http://msdn.microsoft.com/en-us/library/dd783449.aspx )中的 IObserver 接口的语义相匹配。

您的函数可以调用OnNext零次或多次,然后在发生错误时调用OnError ,如果不会引发进一步的事件,则调用OnCompleted 。Excel-DNA 将像处理常规 UDF 抛出的异常一样处理 OnError,并将 #VALUE 返回到单元格或通过注册的 UnhandledExceptionHandler 处理异常。OnCompleted 在 Excel 上下文中不是很有用 - 它只是表明不会产生进一步的值。

对于您的示例,错误似乎不是问题,并且事件流没有尽头,因此您永远不需要调用 OnError 或 OnCompleted。

当 observable 不再连接到单元格公式时,Excel-DNA 基础结构将调用IDisposable.Dispose 。例如,如果从单元格中删除了调用 MyExcelTicker() 的公式。您可以将其用作清理任何后端资源的通知,或者如果您不感兴趣,则忽略该通知。

于 2013-08-03T15:23:22.517 回答