我应该捕获所有可能的特定异常还是只捕获一般异常并将其封装在自定义异常中
本文关键字:异常 自定义异常 封装 有可能 我应该 | 更新日期: 2023-09-27 18:00:35
假设我想将某个XML文件反序列化为强类型对象。如果这个XML文件无法反序列化(无论出于何种原因),我只会创建一个默认对象,并继续正常的应用程序工作流,而不会向用户显示任何错误。(实际上,这个应用程序是作为Windows服务运行的,所以没有用户…例如,想象一下应用程序试图加载配置文件,如果失败,那么只使用默认配置)。
我的问题是如何创建Deserialize()方法,使其易于通过调用代码来使用?我主要关心的是如何处理异常。。。这是我的研究。
解决方案1:
这里是最简单的方法:
public static T Deserialize<T>(string xml)
{
var serializer = new XmlSerializer(typeof (T));
using (var sr = new StreamReader(xml))
using (var reader = XmlReader.Create(sr))
return (T) serializer.Deserialize(reader);
}
我认为在调用代码中很难使用这个方法,因为我必须处理这些方法/构造函数中任何一个都可能引发的所有可能的异常。调用代码并不关心这一点,它只关心操作是否成功。
这是我的第二次尝试:
解决方案2:
public static T Deserialize<T>(string xml)
{
try
{
var serializer = new XmlSerializer(typeof (T));
using (var sr = new StreamReader(xml))
using (var reader = XmlReader.Create(sr))
return (T) serializer.Deserialize(reader);
}
catch (ArgumentException ex)
{
throw new XmlDeserializeException(ValidationExceptionMsg, ex);
}
catch (IOException ex)
{
throw new XmlDeserializeException(ValidationExceptionMsg, ex);
}
catch (XmlSchemaException ex)
{
throw new XmlDeserializeException(ValidationExceptionMsg, ex);
}
catch (InvalidOperationException ex)
{
throw new XmlDeserializeException(ValidationExceptionMsg, ex);
}
}
在这里,我创建了一个名为XmlDeserializeException的自定义异常,并使用它来包装try块中方法(如MSDN中指定的)可以抛出的所有异常。现在,调用代码应该只捕获XmlDeserializeException以知道存在错误。然而,我不确定这个解决方案有多好。。。如果我需要创建很多这样的方法,那么所有这些方法都会有很多catch块,这些块只将异常包装为自定义异常。
所以我想知道以下代码会更好吗:
解决方案3:
public static T Deserialize<T>(string xml)
{
try
{
var serializer = new XmlSerializer(typeof (T));
using (var sr = new StreamReader(xml))
using (var reader = XmlReader.Create(sr))
return (T) serializer.Deserialize(reader);
}
catch (Exception ex)
{
throw new XmlDeserializeException(ValidationExceptionMsg, ex);
}
}
在这里,我捕获一般异常并将其包装在自定义XmlDeserializeException中。通过这种方式,我减少了代码编写,没有代码冗余,也减少了混乱。调用代码将再次只需要像解决方案2中那样捕获XmlDeserializeException。
我应该使用哪种解决方案,为什么?有更好的方法吗?请记住我想要使用这个Deserialize()方法的场景,这不是一个库/框架,而是一个没有用户交互性的应用程序。
如果您以相同的方式处理异常,那么使用不同的捕获确实没有意义,在这种情况下,您可以将混乱降至最低,并使用解决方案3。
解决方案2只是用更多的代码来做完全相同的事情。
在您的问题中,您说:
假设我想将一些XML文件反序列化为强类型对象以防此XML文件无法反序列化(无论原因)我只会创建一个默认对象并继续正常应用程序工作流,而不会向用户显示任何错误。
如果您对异常不做任何操作(正如您所说,没有用户界面,只有一个窗口服务),您可以记录您的异常,并返回default(T)
,而不是传播异常,如下所示:
public static T Deserialize<T>(string xml)
{
try
{
var serializer = new XmlSerializer(typeof (T));
using (var sr = new StreamReader(xml))
using (var reader = XmlReader.Create(sr))
return (T) serializer.Deserialize(reader);
}
catch (Exception ex)
{
// Log exception here:
Logger.Log(...)
return default(T);
}
}
我假设您的Deserialize
操作位于由业务组件调用的技术组件中;所以我会尽可能地保持它的干净和简单:
public static T Deserialize<T>(string xml)
{
try
{
var serializer = new XmlSerializer(typeof (T));
using (var sr = new StreamReader(xml))
using (var reader = XmlReader.Create(sr))
return (T) serializer.Deserialize(reader);
}
catch (Exception)
{
// clean up if needed
throw; // throw and keep stack trace
}
在您的调用者或一般错误处理组件中,您可以捕获exeption;并采取行动;例如,进行日志记录(包括堆栈跟踪),将异常转换为业务异常等。
注意:
- 在
Deserialize
操作中,您重新抛出异常,保留堆栈跟踪 - 在
Deserialize
操作中,抛出异常,不要返回状态。如果它是一个异常,就抛出它,不要吞下它,并依赖状态 - 在调用方中,处理异常并执行任何必须执行的操作:例如,在WCF服务的情况下,将异常转换为故障。只有当您在处理异常时有不同的行为时,才会有不同的捕获