Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何查看我的反应式扩展查询正在做什么?_C#_Debugging_System.reactive - Fatal编程技术网

C# 如何查看我的反应式扩展查询正在做什么?

C# 如何查看我的反应式扩展查询正在做什么?,c#,debugging,system.reactive,C#,Debugging,System.reactive,我正在编写一个包含大量运算符的复杂的反应式扩展查询。我怎么知道发生了什么事 我正在询问和回答这个问题,因为它有相当多的内容,并且可能具有良好的通用性。在开发Rx运营商时,您可以将此功能随意附加到Rx运营商,以查看发生了什么: public static IObservable<T> Spy<T>(this IObservable<T> source, string opName = null) { opName = opName

我正在编写一个包含大量运算符的复杂的反应式扩展查询。我怎么知道发生了什么事


我正在询问和回答这个问题,因为它有相当多的内容,并且可能具有良好的通用性。

在开发Rx运营商时,您可以将此功能随意附加到Rx运营商,以查看发生了什么:

    public static IObservable<T> Spy<T>(this IObservable<T> source, string opName = null)
    {
        opName = opName ?? "IObservable";
        Console.WriteLine("{0}: Observable obtained on Thread: {1}",
                          opName,
                          Thread.CurrentThread.ManagedThreadId);

        return Observable.Create<T>(obs =>
        {
            Console.WriteLine("{0}: Subscribed to on Thread: {1}",
                              opName,
                              Thread.CurrentThread.ManagedThreadId);

            try
            {
                var subscription = source
                    .Do(x => Console.WriteLine("{0}: OnNext({1}) on Thread: {2}",
                                                opName,
                                                x,
                                                Thread.CurrentThread.ManagedThreadId),
                        ex => Console.WriteLine("{0}: OnError({1}) on Thread: {2}",
                                                 opName,
                                                 ex,
                                                 Thread.CurrentThread.ManagedThreadId),
                        () => Console.WriteLine("{0}: OnCompleted() on Thread: {1}",
                                                 opName,
                                                 Thread.CurrentThread.ManagedThreadId)
                    )
                    .Subscribe(obs);
                return new CompositeDisposable(
                    subscription,
                    Disposable.Create(() => Console.WriteLine(
                          "{0}: Cleaned up on Thread: {1}",
                          opName,
                          Thread.CurrentThread.ManagedThreadId)));
            }
            finally
            {
                Console.WriteLine("{0}: Subscription completed.", opName);
            }
        });
    }
给出输出:

Range: Observable obtained on Thread: 7
Range: Subscribed to on Thread: 7
Range: Subscription completed.
Range: OnNext(0) on Thread: 7
Range: OnCompleted() on Thread: 7
Range: Cleaned up on Thread: 7
Range: Observable obtained on Thread: 7
Range: Subscribed to on Thread: 7
Range: OnNext(0) on Thread: 7
Range: OnCompleted() on Thread: 7
Range: Subscription completed.
Range: Cleaned up on Thread: 7
Range: Observable obtained on Thread: 7
Scan: Observable obtained on Thread: 7
Scan: Subscribed to on Thread: 7
Range: Subscribed to on Thread: 7
Range: Subscription completed.
Scan: Subscription completed.
Range: OnNext(1) on Thread: 7
Scan: OnNext(1) on Thread: 7
Range: OnNext(2) on Thread: 7
Scan: OnNext(3) on Thread: 7
Range: OnCompleted() on Thread: 7
Scan: OnCompleted() on Thread: 7
Range: Cleaned up on Thread: 7
Scan: Cleaned up on Thread: 7
但这是:

Observable.Range(0, 1, Scheduler.Immediate).Spy("Range").Subscribe();
给出输出:

Range: Observable obtained on Thread: 7
Range: Subscribed to on Thread: 7
Range: Subscription completed.
Range: OnNext(0) on Thread: 7
Range: OnCompleted() on Thread: 7
Range: Cleaned up on Thread: 7
Range: Observable obtained on Thread: 7
Range: Subscribed to on Thread: 7
Range: OnNext(0) on Thread: 7
Range: OnCompleted() on Thread: 7
Range: Subscription completed.
Range: Cleaned up on Thread: 7
Range: Observable obtained on Thread: 7
Scan: Observable obtained on Thread: 7
Scan: Subscribed to on Thread: 7
Range: Subscribed to on Thread: 7
Range: Subscription completed.
Scan: Subscription completed.
Range: OnNext(1) on Thread: 7
Scan: OnNext(1) on Thread: 7
Range: OnNext(2) on Thread: 7
Scan: OnNext(3) on Thread: 7
Range: OnCompleted() on Thread: 7
Scan: OnCompleted() on Thread: 7
Range: Cleaned up on Thread: 7
Scan: Cleaned up on Thread: 7
找出区别了吗

显然,您可以将其更改为写入日志或进行调试,或者使用预处理器指令对发布版本进行精简传递订阅等

您可以在整个操作符链中应用
Spy
。e、 g:

Observable.Range(0,3).Spy("Range")
          .Scan((acc, i) => acc + i).Spy("Scan").Subscribe();
给出输出:

Range: Observable obtained on Thread: 7
Range: Subscribed to on Thread: 7
Range: Subscription completed.
Range: OnNext(0) on Thread: 7
Range: OnCompleted() on Thread: 7
Range: Cleaned up on Thread: 7
Range: Observable obtained on Thread: 7
Range: Subscribed to on Thread: 7
Range: OnNext(0) on Thread: 7
Range: OnCompleted() on Thread: 7
Range: Subscription completed.
Range: Cleaned up on Thread: 7
Range: Observable obtained on Thread: 7
Scan: Observable obtained on Thread: 7
Scan: Subscribed to on Thread: 7
Range: Subscribed to on Thread: 7
Range: Subscription completed.
Scan: Subscription completed.
Range: OnNext(1) on Thread: 7
Scan: OnNext(1) on Thread: 7
Range: OnNext(2) on Thread: 7
Scan: OnNext(3) on Thread: 7
Range: OnCompleted() on Thread: 7
Scan: OnCompleted() on Thread: 7
Range: Cleaned up on Thread: 7
Scan: Cleaned up on Thread: 7

我相信你可以找到方法来丰富这一点,以满足你的目的。

又过了三年,我仍然在使用你的想法。我的版本现在演变如下:

  • 重载以选择日志记录目标
  • 订阅的日志数
  • 记录来自坏订阅服务器的“下游”异常
守则:

public static IObservable<T> Spy<T>(this IObservable<T> source, string opName = null)
{
    return Spy(source, opName, Console.WriteLine);
}

public static IObservable<T> Spy<T>(this IObservable<T> source, string opName, 
                                                              Action<string> logger)
{
    opName = opName ?? "IObservable";
    logger($"{opName}: Observable obtained on Thread: {Thread.CurrentThread.ManagedThreadId}");

    var count = 0;
    return Observable.Create<T>(obs =>
    {
        logger($"{opName}: Subscribed to on Thread: {Thread.CurrentThread.ManagedThreadId}");
        try
        {
            var subscription = source
                .Do(x => logger($"{opName}: OnNext({x}) on Thread: {Thread.CurrentThread.ManagedThreadId}"),
                    ex => logger($"{opName}: OnError({ex}) on Thread: {Thread.CurrentThread.ManagedThreadId}"),
                    () => logger($"{opName}: OnCompleted() on Thread: {Thread.CurrentThread.ManagedThreadId}")
                )
                .Subscribe(t =>
                {
                    try
                    {
                        obs.OnNext(t);
                    }
                    catch(Exception ex)
                    {
                        logger($"{opName}: Downstream exception ({ex}) on Thread: {Thread.CurrentThread.ManagedThreadId}");
                        throw;
                    }
                }, obs.OnError, obs.OnCompleted);

            return new CompositeDisposable(
                    Disposable.Create(() => logger($"{opName}: Dispose (Unsubscribe or Observable finished) on Thread: {Thread.CurrentThread.ManagedThreadId}")),
                    subscription,
                    Disposable.Create(() => Interlocked.Decrement(ref count)),
                    Disposable.Create(() => logger($"{opName}: Dispose (Unsubscribe or Observable finished) completed, {count} subscriptions"))
                );
        }
        finally
        {
            Interlocked.Increment(ref count);
            logger($"{opName}: Subscription completed, {count} subscriptions.");
        }
    });
}
publicstaticiobservable-Spy(此IObservable源,字符串opName=null)
{
return Spy(source、opName、Console.WriteLine);
}
公共静态IObservable Spy(此IObservable源,字符串opName,
动作记录器)
{
opName=opName??“IObservable”;
记录器($“{opName}:在线程:{Thread.CurrentThread.ManagedThreadId}上获得的可观察到的”);
var计数=0;
返回可观察的。创建(obs=>
{
记录器($“{opName}:在线程上订阅:{Thread.CurrentThread.ManagedThreadId}”);
尝试
{
变量订阅=源
.Do(x=>logger($”{opName}:OnNext({x})在线程:{Thread.CurrentThread.ManagedThreadId})上的,
ex=>记录器($“{opName}:OnError({ex})在线程:{Thread.CurrentThread.ManagedThreadId}”)上,
()=>记录器($“{optname}:OnCompleted()在线程:{Thread.CurrentThread.ManagedThreadId}”上)
)
.订阅(t=>
{
尝试
{
obs.OnNext(t);
}
捕获(例外情况除外)
{
记录器($“{opName}:线程{Thread.CurrentThread.ManagedThreadId}”上的下游异常({ex}”);
投掷;
}
},obs.OnError,obs.OnCompleted);
返回新的CompositeDisposable(
一次性.Create(()=>logger($“{opName}:在线程:{Thread.CurrentThread.ManagedThreadId}”)上处置(取消订阅或可观察完成),
订阅
一次性.Create(()=>Interlocked.decreation(ref count)),
一次性.Create(()=>logger($“{opName}:Dispose(取消订阅或可观察完成)已完成,{count}订阅”))
);
}
最后
{
联锁增量(参考计数);
记录器($“{opName}:订阅已完成,{count}订阅。”);
}
});
}

如果
Do(x=>Console.WriteLine(…)
还不够,这是一个非常好的解决方案。我通过将订阅放在一个字段中,然后将其包装在一个带有控制台的CompositeDisposable中来“充实”订阅。WriteLine可能会因为记录取消订阅而感到难过。很好,很好!需要注意的一点是,
Create
在您返回的
IDisposable
上调用
Dispose()
,即使订户没有
Dispose
将在订阅者Dispose或observable终止时调用-以最快发生的为准。它这样做是为了让你能尽快清理资源——所以请记住,这可能不是退订的结果。回答不错。我还发现订阅和处理(或缺少)是调试Rx查询的关键元素。当您选择了许多您可能认为已导入/完成的子句时,这一点尤为重要。这种日志记录/间谍活动消除了猜测工作和假设。再次回来说这种扩展方法仍然经常“拯救我的生命”:)很高兴听到它如此有用,而且装饰得很好!