C#中异常的高阶函数方法

时间:2009-10-21 21:33:39

标签: c# exception-handling

嘿大家,我正在考虑将方法块作为参数传递给辅助类,这些辅助类是在异常处理中构建的,但它是直观的东西之一,我想将其提交给批评,见解或建议。 / p>

我想提前注意,这是我如何处理所有异常处理,但有些情况下我发现这种结构更具“可读性”。

例如,我有一个场景,我正在显示预览图像,但如果失败(这是我正在预览图像并且无法预览某些GIF / BMP格式的真实场景),它只是我的场景显示备用图像而不是预览。 try / catch代码块如下所示:

  try
  {
        alternatePreviewImage.SetSource(fs);
  }
  catch (Exception ex) {
        requiresSpecialPreview = false;
        previewImage = new BitmapImage(new Uri("Images/NoPreviewAvailable.png", UriKind.Relative));
  }

所以我将利用一个带有方法参数的辅助类来使它看起来像这样:

  if(!ErrorHelper.RunWithSuccessNotify(()=> alternatePreviewImage.SetSource(fs))){
        requiresSpecialPreview = false;
        previewImage = new BitmapImage(new Uri("Images/NoPreviewAvailable.png", UriKind.Relative));                             
  }

ErrorHelper.RunWithSuccessNotify非常简单:

public static bool RunWithSuccessNotify(Action code) {
    bool success = true;
    try
    {
        code();
    }
    catch (Exception ex)
    {
        success = false;
    }

    return success;
}

让我再次强调,这对于这些低影响情景以及我可能能够抑制异常的其他情况非常有用:

public static void RunWithErrorSuppression(Action code) {
    try
    {
        code();
    }
    catch (Exception ex)
    {
        // pass
    }
}

这种方法也可以更详细,以便捕获异常:

    public static void ExecuteWithLogging(Action code, Action<Exception> handles) {
        try
        {
            code();
        }
        catch (Exception ex)
        {
            handles(ex);
        }
    }

那么关于集中异常处理的这套策略有什么想法?如果这是一个糟糕的方向,是否有特定的原因可能导致我遇到麻烦?

6 个答案:

答案 0 :(得分:3)

我采用这种方法的主要问题是捕获Exception通常被视为错误形式。如果有某种方法可以指定要捕获哪种类型的异常,我可能会购买它,但我认为必须在编译时指定。对于捕获所有异常并重新抛出那些不匹配的异常,我也感到不自在。

请记住,当您捕获异常时,您实际上是在说您可以以某种有意义的方式处理错误。显然,上面的代码无法处理StackOverflowExceptionMissingMethodException

答案 1 :(得分:1)

不要捕获所有异常,参数化你想要的那个:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ProvaException
{
    class Program
    {
        static void Main(string[] args)
        {

            int a = doWithException<int>(divide, 0, typeof(DivideByZeroException), x => 300);
            Console.WriteLine(a);
            Console.ReadKey();
        }

        public static int divide(int b)
        {
            return 10 / b;
        }

        public static T doWithException<T>(Func<T, T> a, T param1, Type exType, Func<Exception, T> handFunction) {
            try
            {
                return a(param1);
            }
            catch(Exception ex) {
                if(exType.Equals(ex.GetType())) {
                     return handFunction(ex);
                }
                else 
                    throw ex;
            }
        }
    }
}

不是很喜欢C#,但它在相同的情况下很有用(当你想抽象处理异常时)。

您必须为每种类型的函数编写不同的类型签名,因为C#不支持currying。 Veeeeeeeeery无聊。 :d

这不是一个非常优雅的解决方案,但它提供了一些关于功能语言中的工作原理的提示。学习起来非常有趣。

答案 2 :(得分:0)

您好我不知道它是否可以很好地扩展但是现在我的团队正在我们的测量软件中使用它来处理特殊异常(例如丢失一些测量设备的通信)并在这种情况下重新开始测量。

现在它运行良好,特别是在代码的重要/非重复部分被调用的函数(Action参数)而不是异常处理的情况下。

最好的情况是它们甚至可以嵌套,主代码仍然可读。

另一方面,你隐藏了你的异常处理在函数名后面做了什么,所以它应该简单或清晰,只能通过函数的名称来理解,否则你会混淆未来读者的某些东西你的代码(包括你自己)

答案 3 :(得分:0)

我最初的想法只是传递复杂的匿名代表闻起来很糟糕。没有什么可以阻止某人将具有复杂逻辑的匿名委托转储到ErrorHelper方法中。然后这种逻辑变得难以测试。我希望逻辑只能在一个对象中传递,这对我来说似乎是一种更好理解的技术。对我来说,匿名委托(和lambdas)是非常简单的逻辑可扩展性,就像比较逻辑一样。匿名代表的机制是强大的,可以用于其他事情,但强大的力量带来了巨大的责任。 =)

您使用技术实现的是能够在运行时干净地更改异常处理策略。但这是一个实际要求吗?

答案 4 :(得分:0)

看一下THIS并且可能会把它上面的''反射器'扯掉一点。在我看来,就像在那里完成相同的事情,但可能以更完整的方式......看起来非常强大......

答案 5 :(得分:-1)

如果我已经理解了你要做的事情,那么作为一种标准的错误管理模式对我来说感觉很糟糕。这是一个警示标志,因为我花了一些时间来掌握你的想法。

除非有充分的理由,否则应在方法中明确提供代码,供任何维护开发人员查看和修改,而不是从其他位置传递。仅仅因为存在一个特征并不意味着它在大多数情况下都很有用。在这种情况下,我看不到混淆带来的任何好处。

顺便说一句,我想你是nailing the corpse to the wall by catching System.Exception。正常规则不是捕获异常,除非您知道异常是什么和/或您出于某种原因需要知道异常细节。

如果出现未知异常,常用的恢复模式如下所示:

bool exceptionHappened = true;
try
{
    alternatePreviewImage.SetSource(fs);
    exceptionHappened = false;
}
finally
{
    if ( exceptionHappened )
    {    
        requiresSpecialPreview = false;
        etc;
    }
}