【问题标题】:Problem with too many try catch太多try catch的问题
【发布时间】:2010-12-20 13:17:07
【问题描述】:

是否可以编写像outType? TryDo(func, out exception, params) 这样的方法,调用func(arg1,arg2,arg3,...),其中params 包含arg1,arg2,arg3,...,然后返回func 返回值,如果发生任何异常返回null 并设置异常?

这可以通过另一个函数签名更好地完成吗?

例如我有 string Foo1(int i) { return i.ToString()}

void Foo2(int[] a) {throw new Exception();}

然后调用

string t = TryDo(Foo1, out ex, {i});

TryDo(Foo2, out ex, {});

-----------已编辑-------

        string t;
        SomeClass c;
        try
        {
            t = Foo1(4, 2, new OtherClass());
        }
        catch (Exception ex)
        {
            Log(ex);
            if (/*ex has some features*/)
                throw ex;
        }

        try
        {
            Foo2();
        }
        catch (Exception ex)
        {
            Log(ex);
            if (/*ex has some features*/)
                throw ex;
        }
        .
        .
        .

我想变成这样。

        string t = TryDo(Foo1, out ex, {4, 2, new OtherClass());
        Examine(ex);
        SomeClass c = TryDo(Foo2, out ex, {});
        Examine(ex);

【问题讨论】:

  • 如果你的代码中有太多的 try/catch 块,你很可能做错了什么。您应该只捕获您可以实际处理的异常并让所有其他异常传播。
  • +1 @ Brian Rasmussen。另外,请注意,如果您从单个方法处理多个异常,则不必嵌套捕获。例如:try { /* file i/o */ } catch (AccessDenied ex){} catch (FileNotFound ex){} catch (IOException ex){} // etc
  • (如果你在做某种 I/O,如果健壮性很重要,你总是会有很多错误处理)
  • @brian:如果我确定应该传播异常,然后我再次抛出,不要试图通过删除它来回答问题。
  • @HPT:你能给我们举个例子说明问题中的“too many try-catch”是什么样的吗?

标签: c# .net c#-4.0


【解决方案1】:

您的问题表明您误解了应如何处理异常。到处使用 try/catch 会产生不希望的结果,并使您的应用程序更难调试。

简而言之,只在以下情况下处理异常:

  1. 您可以处理异常并返回承诺的结果
  2. 捕获层特定异常并用更通用的异常替换它们 (SqlException -> DataSourceException)
  3. 在顶层可以全部捕获
  4. 在线程中捕获一切正常(因为线程中未捕获的异常会使您的应用崩溃)

更多信息在我的博客中:http://blog.gauffin.org/2010/11/do-not-catch-that-exception/

更新

不要使用throw ex。您正在破坏原始调用堆栈,因此隐藏了最初引发异常的位置。 throw; 是你的小狗。随时随地使用它。

【讨论】:

  • 如果需要调试,我会记录异常。
  • throw; 抛出当前在上下文中的异常而不重置调用堆栈。这在许多调试场景中非常有用。
  • @HPT:当然。记录异常是获取调试信息的一种方式。但是,如果您在任何地方都尝试/捕获/重新抛出,您可能最终会多次记录相同的异常。随着应用程序的增长,它变得难以调试。
【解决方案2】:

除非绝对必要,否则我会避免使用out 参数。

这是Design Guidelines for Developing Framework Libraries的引述:

避免使用 out 或 reference 参数。

使用定义输出或引用参数的成员需要开发人员了解指针、值类型和引用类型之间的细微差别,以及输出和引用参数之间的初始化差异。

您可以改为创建一个包装调用结果的返回类型:

class CallResult<T> where T : class {
  public CallResult(T result) { Result = result; }
  public CallResult(Exception exception) { Exception = exception; }
  public T Result { get; private set; }
  public Exception Exception { get; private set; }
  public Boolean IsSuccessful { get { return Exception == null; } }
}

你的方法可以这样实现:

CallResult<T> TryDo<T>(Func<Object[], T> action, params Object[] args) where T : class {
  try {
    return new CallResult<T>(action(args));
  }
  catch (Exception ex) {
    return new CallResult<T>(ex);
  }
}

你可以这样称呼它:

var callResult = TryDo<String>(Foo1, 4, 2, new OtherClass());
if (!callResult.IsSuccessful)
  Examine(callResult.Exception);

但是,如果您打算在 Examine 方法中重新抛出异常,从而丢失堆栈跟踪您应该重新考虑您的方法

【讨论】:

  • 一般来说,是的。 bool TrySomething(params, out result); 模式几乎是我在 C# 中使用 out 参数知道的唯一完善的最佳实践。
  • @Martin:你是这里唯一试图解决问题的人! tnx,如果这里没有建议更好的解决方案,我会将其标记为答案。
  • @Martin:为什么要避免使用 out 参数?
  • out 参数在用户空间中很容易出错,并且通常不遵循 C# 代码的行业标准设计指南。
  • 函数编译错误 使用泛型类型System.Func&lt;Tresult&gt;需要1个类型参数
【解决方案3】:

是的,这是可能的。

但是你为什么要以这种方式返回一个可能的异常呢?您可以在需要的地方进一步抛出和处理。

【讨论】:

    【解决方案4】:

    是的,但你为什么要这样?

    int? TryDo(delegate d, out Exception e, params object[] par)  
    {
       try
       {
          int res = d.Invoke(par);
          e = null;
          return res;
       }  
       catch(Exception ex) { e = ex; return null; }
    }
    

    【讨论】:

    • 它只是返回 int!返回字符串或类的函数怎么样?
    • d 没有Invoke() 它有DynamicInvoke()
    【解决方案5】:

    如果你有太多的 try...catch(我不明白为什么)你可以使用 AOP 来集中处理异常。

    您可以在下面找到说明如何使用它的链接:

    http://www.codeproject.com/KB/architecture/ExceptionHandlingWithAOP.aspx

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-23
      • 1970-01-01
      • 1970-01-01
      • 2011-11-28
      • 2011-08-26
      • 2011-09-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多