可观察管道中的异常处理

2024-03-22

我创建了一个可观察对象,其中包含通过运行异步方法将一个项目转换为另一个项目。

IObservable<Summary> obs = scanner.Scans
                    .SelectMany(b => GetAssignment(b))
                    .SelectMany(b => VerifyAssignment(b))
                    .SelectMany(b => ConfirmAssignmentData(b))
                    .SelectMany(b => UploadAsset(b))
                    .Select(assignment => new Summary())
                    .Catch(LogException());

我想让这个防失败,所以如果在处理过程中抛出异常,我应该记录异常,但忽略异常并继续下一次扫描(由scanner.Scans)

当前代码捕获任何异常,但一旦引发异常,序列就会结束。

我怎样才能让它“吞掉”异常(记录它),但继续下一个项目?


Rx 是一种函数式范例,因此使用函数式方法来解决这个问题非常有用。

答案是引入另一个可以处理错误的 monad,例如Nullable<T>可以处理具有空值的整数,但在这种情况下,类可以表示值或异常。

public class Exceptional
{
    public static Exceptional<T> From<T>(T value) => new Exceptional<T>(value);
    public static Exceptional<T> From<T>(Exception ex) => new Exceptional<T>(ex);
    public static Exceptional<T> From<T>(Func<T> factory) => new Exceptional<T>(factory);
}

public class Exceptional<T>
{
    public bool HasException { get; private set; }
    public Exception Exception { get; private set; }
    public T Value { get; private set; }

    public Exceptional(T value)
    {
        this.HasException = false;
        this.Value = value;
    }

    public Exceptional(Exception exception)
    {
        this.HasException = true;
        this.Exception = exception;
    }

    public Exceptional(Func<T> factory)
    {
        try
        {
            this.Value = factory();
            this.HasException = false;
        }
        catch (Exception ex)
        {
            this.Exception = ex;
            this.HasException = true;
        }
    }

    public override string ToString() =>
        this.HasException
            ? this.Exception.GetType().Name
            : (this.Value != null ? this.Value.ToString() : "null");
}


public static class ExceptionalExtensions
{
    public static Exceptional<T> ToExceptional<T>(this T value) => Exceptional.From(value);

    public static Exceptional<T> ToExceptional<T>(this Func<T> factory) => Exceptional.From(factory);

    public static Exceptional<U> Select<T, U>(this Exceptional<T> value, Func<T, U> m) =>
        value.SelectMany(t => Exceptional.From(() => m(t)));

    public static Exceptional<U> SelectMany<T, U>(this Exceptional<T> value, Func<T, Exceptional<U>> k) =>
        value.HasException ? Exceptional.From<U>(value.Exception) : k(value.Value);

    public static Exceptional<V> SelectMany<T, U, V>(this Exceptional<T> value, Func<T, Exceptional<U>> k, Func<T, U, V> m) =>
        value.SelectMany(t => k(t).SelectMany(u => Exceptional.From(() => m(t, u))));
}

因此,我们首先创建一个抛出异常的 Rx 查询。

IObservable<int> query =
    Observable
        .Range(0, 10)
        .Select(x => 5 - x)
        .Select(x => 100 / x)
        .Select(x => x + 5);

如果我运行可观察的结果,我会得到:

让我们用以下方法来改变它:Exceptional并看看它如何允许我们在发生错误时继续处理。

IObservable<Exceptional<int>> query =
    Observable
        .Range(0, 10)
        .Select(x => x.ToExceptional())
        .Select(x => x.Select(y => 5 - y))
        .Select(x => x.Select(y => 100 / y))
        .Select(x => x.Select(y => y + 5));

现在当我运行它时我得到这个:

现在我可以测试每个结果,看看是否HasException is true并记录每个异常,同时可观察的继续。

最后,通过引入另一种扩展方法,可以轻松清理查询,使其看起来与原始查询几乎相同。

    public static IObservable<Exceptional<U>> Select<T, U>(this IObservable<Exceptional<T>> source, Func<T, U> m) =>
        source.Select(x => x.SelectMany(y => Exceptional.From(() => m(y))));

这将可观察到的情况和异常情况结合到一个单一的Select操作员。

现在查询可以如下所示:

IObservable<Exceptional<int>> query =
    Observable
        .Range(0, 10)
        .Select(x => x.ToExceptional())
        .Select(x => 5 - x)
        .Select(x => 100 / x)
        .Select(x => x + 5);

我之前得到了同样的结果。


最后,我可以通过添加另外两个扩展方法来使用查询语法来实现这一切:

public static IObservable<Exceptional<U>> SelectMany<T, U>(this IObservable<T> source, Func<T, Exceptional<U>> k) =>
    source.Select(t => k(t));

public static IObservable<Exceptional<V>> SelectMany<T, U, V>(this IObservable<T> source, Func<T, Exceptional<U>> k, Func<T, U, V> m) =>
    source.SelectMany(t => k(t).SelectMany(u => Exceptional.From(() => m(t, u))));

这允许:

IObservable<Exceptional<int>> query =
    from n in Observable.Range(0, 10)
    from x in n.ToExceptional()
    let a = 5 - x
    let b = 100 / a
    select b + 5;

我再次得到与以前相同的结果。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

可观察管道中的异常处理 的相关文章

  • 包装迭代句柄以在基于范围的 for 循环中使用

    我使用一个带有迭代功能的 API 使用void handle void handle BrowseInit while BrowseGetNext handle int x BrowseGetData handle BrowseFree h
  • 迭代和遍历有什么区别?

    过去几周我一直在学习迭代器 我仍然不明白迭代链接列表和遍历链接列表之间的主要区别 我知道遍历意味着遍历 访问每个元素 链接列表 并且在迭代时基本上做同样的事情 但是有什么不同 为什么不能遍历所有内容 标准库数据结构 遍历 只是意味着遍历数据
  • 非类型模板参数...那是模板! (C++)

    我基本上希望为通用 C 函数生成一个包装器 而无需手动指定类型 所以我有一个带有固定原型的回调 但我需要根据包装函数的类型在包装器中执行一些特殊代码 所以基本上我正在考虑在类模板中使用静态方法将我的函数包装到一致的接口 例如 this is
  • 如何高效生成总和在指定范围内的所有组合(在所有深度)

    假设您有一组值 1 1 1 12 12 16 如何生成总和在预定义范围内的所有可能组合 不重复 min max 例如 这里是 所有深度的 范围在13 and 17 1 12 1 1 12 1 1 1 12 16 1 16 这假设具有相同值的
  • 英特尔融核上的 MKL 性能

    我有一个例程 对小矩阵 50 100 x 1000 个元素 执行一些 MKL 调用以拟合模型 然后我调用不同的模型 在伪代码中 double doModelFit int model while done cblas dgemm cblas
  • 将 MVC 操作结果发送到打印机

    我有一个带有操作的控制器 SomeController ActionToBePrinted ActionToBePrinted 返回一个 html 视图 当按下按钮时 从普通的 mvc razor 视图调用此操作 当按下按钮时 我将如何将视
  • Android 原生 AAssetManager 的文件层次结构

    Issue 我想知道如何从本机代码创建 Android 中资产文件夹的文件层次结构 我在用着AAssetManager openDir but AAssetDir getNextFileName不返回任何目录名称 因此基本上我无法深入了解层
  • MongoDB C# 驱动程序检查身份验证状态和角色

    这是我使用 MongoDB 身份验证机制登录 MongoDB 的代码 try var credential MongoCredential CreateMongoCRCredential test admin 123456 var sett
  • .net 4.5 可以与 .net 4.0 并行工作吗?

    我有兴趣安装 NET 4 5 但我听说这是就地升级 由于我公司的用户使用的是Windows XP 我无法发布任何使用 NET 4 5 的客户端应用程序 http visualstudio uservoice com forums 12157
  • 从 SQL 数据库获取日期时间

    我的数据库表中有一个 DateTime 记录 我编写一个查询从数据库中获取它 string command2 select Last Modified from Company Data where Company Name Descrip
  • 如何调试参数化 SQL 查询

    我使用 C 连接到数据库 然后使用 Ad hoc SQL 来获取数据 这个简单的 SQL 查询非常方便调试 因为我可以记录 SQL 查询字符串 如果我使用参数化 SQL 查询命令 有没有办法记录 sql 查询字符串以进行调试 我想就是这样的
  • 处理 LINQ sum 表达式中的 null

    我正在使用 LINQ 查询来查找列的总和 并且在少数情况下该值有可能为空 我现在使用的查询是 int score dbContext domainmaps Where p gt p SchoolId schoolid Sum v gt v
  • .net 运行时 - Silverlight 运行时 =?

    我用 google 搜索了一下 但没能找到 net CLR 中的哪些类未包含在 CoreCLR 又名 Silverlight 中的详细列表 Windows net Framework 中缺少什么 Silverlight 另外 是否存在 Si
  • .NET 可移植类库中的 .ToShortDateString 发生了什么

    我想知道为什么没有 ToShortDateString在 NET 可移植类库中 我有 2 个项目 Silverlight 和常规 NET 类库 使用相同的代码 并且代码涉及调用 ToShortDateString on a DateTime
  • Linq Where 本地计数器关闭在 VS watch 中的结果不同

    我尝试删除前 3 个元素array与 LinQWhere扩展功能 这是一个例子 var array new 1 2 3 4 5 6 7 8 9 var count 3 var deletedTest1 0 var test1 array W
  • C++ 在 Vector 中使用不可分配的对象

    我想将对象列表存储在std vector 但对象包含引用且无法分配给 但是 我可以复制构造该对象 我能想到的唯一选择是使用指针来包装对象并在需要分配指针时重新设置指针 但这样做的语法会显着降低可读性 特别是在使用迭代器时 我更喜欢另一种选择
  • 控制台应用程序 .net Core 2.0 的配置

    在 net Core 1 中我们可以这样做 IConfiguration config new ConfigurationBuilder AddJsonFile appsettings json true true Build 这样就可以使
  • C中有const吗?

    这个问题可能很幼稚 但是 有没有constC 中的关键字 从哪个版本开始 之间有任何语义和 或句法差异吗const在 C 和 C 中 C 和 C 之间在语法上没有差异const关键字 除了一个相当晦涩的关键字 在 C 中 自 C99 起 您
  • glDrawElements 只绘制半个四边形

    这是我的功能 void Object draw2 if mIsInitialised return Tell OpenGL about our vertex and normal data glEnableClientState GL VE
  • 防止复制构造和返回值引用的分配

    如果我有一个函数返回对类实例的引用 但我无法控制其源 比如说list

随机推荐