c#exception处理,实例。 你会怎么做?

我正试图在处理exception方面做得更好,但是当我尽力抓住它们时,我觉得我的代码变得非常丑陋,难以理解和混乱。 我很想看看其他人如何通过提供实际例子和比较解决方案来解决这个问题。

我的示例方法从URL下载数据并尝试将其序列化为给定类型,然后返回填充了数据的实例。

首先,没有任何exception处理:

private static T LoadAndSerialize(string url) { var uri = new Uri(url); var request = WebRequest.Create(uri); var response = request.GetResponse(); var stream = response.GetResponseStream(); var result = Activator.CreateInstance(); var serializer = new DataContractJsonSerializer(result.GetType()); return (T)serializer.ReadObject(stream); } 

我觉得这个方法很可读。 我知道这个方法中有一些不必要的步骤(比如WebRequest.Create()可以接受一个字符串,我可以链接方法而不给它们变量)但是我会这样做,以便更好地与具有exception的版本进行比较 – 处理。

这是处理可能出错的一切的第一次尝试:

  private static T LoadAndSerialize(string url) { Uri uri; WebRequest request; WebResponse response; Stream stream; T instance; DataContractJsonSerializer serializer; try { uri = new Uri(url); } catch (Exception e) { throw new Exception("LoadAndSerialize : Parameter 'url' is malformed or missing.", e); } try { request = WebRequest.Create(uri); } catch (Exception e) { throw new Exception("LoadAndSerialize : Unable to create WebRequest.", e); } try { response = request.GetResponse(); } catch (Exception e) { throw new Exception(string.Format("LoadAndSerialize : Error while getting response from host '{0}'.", uri.Host), e); } if (response == null) throw new Exception(string.Format("LoadAndSerialize : No response from host '{0}'.", uri.Host)); try { stream = response.GetResponseStream(); } catch (Exception e) { throw new Exception("LoadAndSerialize : Unable to get stream from response.", e); } if (stream == null) throw new Exception("LoadAndSerialize : Unable to get a stream from response."); try { instance = Activator.CreateInstance(); } catch (Exception e) { throw new Exception(string.Format("LoadAndSerialize : Unable to create and instance of '{0}' (no parameterless constructor?).", typeof(T).Name), e); } try { serializer = new DataContractJsonSerializer(instance.GetType()); } catch (Exception e) { throw new Exception(string.Format("LoadAndSerialize : Unable to create serializer for '{0}' (databinding issues?).", typeof(T).Name), e); } try { instance = (T)serializer.ReadObject(stream); } catch (Exception e) { throw new Exception(string.Format("LoadAndSerialize : Unable to serialize stream into '{0}'.", typeof(T).Name), e); } return instance; } 

这里的问题是,尽管可能出错的一切都会被抓住并且给出一个有意义的例外,但这是一个非常重要的混乱。

那么,如果我把捕获链接起来怎么办呢。 我的下一次尝试是这样的:

  private static T LoadAndSerialize(string url) { try { var uri = new Uri(url); var request = WebRequest.Create(uri); var response = request.GetResponse(); var stream = response.GetResponseStream(); var serializer = new DataContractJsonSerializer(typeof(T)); return (T)serializer.ReadObject(stream); } catch (ArgumentNullException e) { throw new Exception("LoadAndSerialize : Parameter 'url' cannot be null.", e); } catch (UriFormatException e) { throw new Exception("LoadAndSerialize : Parameter 'url' is malformed.", e); } catch (NotSupportedException e) { throw new Exception("LoadAndSerialize : Unable to create WebRequest or get response stream, operation not supported.", e); } catch (System.Security.SecurityException e) { throw new Exception("LoadAndSerialize : Unable to create WebRequest, operation was prohibited.", e); } catch (NotImplementedException e) { throw new Exception("LoadAndSerialize : Unable to get response from WebRequest, method not implemented?!.", e); } catch(NullReferenceException e) { throw new Exception("LoadAndSerialize : Response or stream was empty.", e); } } 

虽然它在眼睛上肯定更容易,但我在这里大量倾向于智能感知,以提供可能从方法或类中抛出的所有exception。 我不相信这个文档是100%准确的,如果某些方法来自.net框架之外的程序集,我会更加怀疑。 例如,DataContractJsonSerializer在intellisense上没有显示exception。 这是否意味着构造函数永远不会失败? 我可以肯定吗?

与此相关的其他问题是某些方法抛出相同的exception,这使得错误更难描述(这或者这个或者这个错误),因此对用户/调试器没那么有用。

第三种选择是忽略除允许我采取重试连接之类的操作之外的所有exception。 如果url为null,则url为null,捕获的唯一好处是更详细的错误消息。

我很乐意看到你的想法和/或实施!

规则之一的exception处理 – 不捕获您不知道如何处理的exception。

仅仅为了提供好的错误消息而捕获exception是值得怀疑的。 exception类型和消息已经包含了开发人员的足够信息 – 您提供的消息不会添加任何值。

DataContractJsonSerializer在intellisense上没有显示exception。 这是否意味着构造函数永远不会失败? 我可以肯定吗?

不,你不能确定。 通常,C#和.NET不像Java那样必须声明可能引发的exception。

第三种选择是忽略除允许我采取重试连接之类的操作之外的所有exception。

这确实是最好的选择。

您还可以在应用程序的顶部添加一个通用exception处理程序,它将捕获所有未处理的exception并记录它们。

首先,阅读我关于exception处理的文章:

http://ericlippert.com/2008/09/10/vexing-exceptions/

我的建议是:您必须处理代码可能抛出的“烦恼exception”和“外部exception”。 Vexing例外是“非例外”例外,所以你必须处理它们。 由于您无法控制的考虑因素,可能会发生外部exception,因此您必须处理它们。

不能处理致命和愚蠢的例外。 你不需要处理的骨头exception,因为你永远不会做任何导致它们被抛出的事情 。 如果他们被抛出然后你有一个错误 ,解决方案是修复错误 。 不要处理exception; 那隐藏了这个bug。 并且你无法有意义地处理致命的例外,因为它们是致命的 。 这个过程即将结束。 您可能会考虑记录致命exception,但请记住, 日志记录子系统可能是首先触发致命exception的事情。

简而言之:只处理那些你知道如何处理的exception。 如果您不知道如何处理它,请将它留给您的来电者; 来电者可能比你知道得更好。

在您的特定情况下:不要处理此方法中的任何exception。 让调用者处理exception。 如果调用者通过了一个无法解析的URL,请将其崩溃 。 如果坏url是一个错误,那么调用者就有一个错误需要修复,你可以通过引起他们的注意来帮助他们。 如果坏url不是错误 – 例如,因为用户的互联网连接混乱了 – 那么调用者需要通过询问真实exception来找出获取失败原因 。 来电者可能知道如何恢复,所以请帮助他们。

首先,出于所有实际目的,您应该永远不会抛出类型Exception 。 总是抛出更具体的东西。 甚至ApplicationException也会更好。 其次,当且仅当调用者有理由关注哪个操作失败时,对不同的操作使用单独的catch语句。 如果在程序中的某一点发生的InvalidOperationException将暗示您的对象状态与其他时间发生的不同,并且如果您的调用者将关心区别,那么您应该包装第一部分你的程序在’try / catch’块中,它会将InvalidOperationException包装在其他一些(可能是自定义的)exception类中。

“只捕获你知道如何处理的exception”的概念在理论上是很好的,但遗憾的是大多数exception类型对于底层对象的状态都是如此模糊,以至于几乎不可能知道一个人是否可以“处理”exception。 例如,可能有一个TryLoadDocument例程,如果无法加载文档的某些部分,则必须在内部使用可能抛出exception的方法。 在发生此类exception的99%的情况下,“处理”此类exception的正确方法是简单地放弃部分加载的文档并返回而不将其暴露给调用者。 不幸的是,很难确定1%的情况是不够的。 你应该努力在你的例行程序失败而不做任何事情的情况下抛出不同的例外,而不是那些可能有其他不可预测的副作用的例外; 不幸的是,你可能会被猜测从你调用的例程中解释大多数exception。

exceptione.message应该有足够的错误消息数据供您正确调试。 当我进行exception处理时,我通常只使用一些关于它发生位置的简短信息和实际exception来记录它。

我不会那样分裂,只是弄得一团糟。 例外主要是针对你的。 理想情况下,如果您的用户导致exception,您可能会提前捕获它们。

我不建议抛出不同的命名exception,除非它们不是真正的exception(例如,有时在某些API调用中响应变为null。我通常会检查它并为我抛出一个有用的exception)。

看看Unity Interception 。 在该框架内,您可以使用称为ICallHandler东西,它允许您拦截调用并执行您需要/想要对截获的调用执行的任何操作。

例如:

 public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { var methodReturn = getNext().Invoke(input, getNext); if (methodReturn.Exception != null) { // exception was encountered... var interceptedException = methodReturn.Exception // ... do whatever you need to do, for instance: if (interceptedException is ArgumentNullException) { // ... and so on... } } } 

当然还有其他拦截框架。

考虑将方法拆分为较小的方法,以便对相关错误进行error handling。

您在同一方法中发生了多个半无关的事情,因此每行代码的error handling必须多于少于或少。

即您的情况,您可以将方法拆分为:CreateRequest(此处处理无效参数错误),GetResponse(处理网络错误),ParseRespone(处理内容错误)。

我不同意@oded说:

“规则之一的exception处理 – 不要捕获你不知道如何处理的exception。”

它可能适用于学术目的,但在现实生活中,您的客户不希望在他们的脸上出现非信息性错误。

我认为您可以而且应该捕获exception,并为用户生成一些信息性exception。 当向用户显示一个很好的错误信息时,它可以获得有关他/她应该采取哪些措施来解决问题的更多信息。

此外,当您决定记录错误时,捕获所有exception会很有用,或者更好的是,它们会自动发送给您。

我的所有项目都有一个Error类,我总是使用它来捕获每个exception。 尽管我在这门课上做的不多,但它可以用于很多事情。