【发布时间】:2015-02-16 09:58:33
【问题描述】:
我正在开发一种插件架构,以允许通过使用适当的插件来执行异构任务,该插件将从一个或多个库(来自指定目录的 DLL 文件)加载。
首先我定义了每个插件必须实现的接口,因此我创建了以下 C# 接口。
public interface ITaskProcessor
{
string Name { get; }
string Version { get; }
string Author { get; }
string Description { get; }
void Execute(Stream sourceStream, Stream destStream);
}
本质上,在定义这个接口时,我假设与任务相关的数据必须预先存储在一个文件中,并且结果将存储在另一个文件中。为此,Execute 方法需要两个Stream 对象作为参数。
两个流的打开和关闭是所有插件通用的操作,所以我决定在TaskProcessorContext类中执行,如下。
public class TaskProcessorContext
{
private ITaskProcessor m_TaskProcessor;
public TaskProcessorContext(ITaskProcessor executor)
{
m_TaskProcessor = executor;
}
public void Execute(string sourceFileName, string destFileName)
{
InternalExecute(sourceFileName, destFileName);
}
private void InternalExecute(string sourceFileName, string destFileName)
{
FileStream sourceStream = null;
FileStream destStream = null;
try
{
sourceStream = File.OpenRead(sourceFileName);
destStream = new FileStream(destFileName, FileMode.Create, FileAccess.Write);
m_TaskProcessor.Execute(sourceStream, destStream); // invoking the method of a plug-in
}
catch (ArgumentNullException e)
{
throw new ArgumentNullException(e.ParamName, e.Message);
}
catch (ArgumentException e)
{
throw new ArgumentException(e.Message, e.ParamName, e);
}
catch (FileNotFoundException e)
{
throw new FileNotFoundException(e.Message, e.FileName, e);
}
catch (DirectoryNotFoundException e)
{
throw new DirectoryNotFoundException(e.Message, e);
}
catch (PathTooLongException e)
{
throw new PathTooLongException(e.Message, e);
}
catch (UnauthorizedAccessException e)
{
throw new UnauthorizedAccessException(e.Message, e);
}
catch (NotSupportedException e)
{
throw new NotSupportedException(e.Message, e);
}
catch (IOException e)
{
throw new IOException(e.Message, e);
}
catch (System.Security.SecurityException e)
{
throw new System.Security.SecurityException(e.Message, e);
}
finally
{
if (sourceStream != null)
{
sourceStream.Close();
sourceStream.Dispose();
sourceStream = null;
}
if (destStream != null)
{
destStream.Close();
destStream.Dispose();
destStream = null;
}
}
}
}
我认为捕获并重新抛出在打开流期间可能发生的所有异常是合适的,将引发的异常作为内部异常传递以避免丢失堆栈跟踪。
如果两个流都正确打开,即打开流时没有出现异常,则调用某个插件的Execute方法。
显然,您无法提前知道每个插件执行的处理操作,因此甚至无法提前知道此类处理过程中可能发生的异常。但是,我需要一种方法来知道在执行插件的Execute 方法期间发生的任何错误:换句话说,我希望每个插件的Execute 方法可能会抛出额外的异常,但是TaskProcessorContext.Execute 方法应该捕获它并重新抛出它而不会丢失堆栈跟踪...
在我看来,管理此问题的唯一方法是定义一个类以用于插件抛出的异常。例如,我可以创建类TaskProcessingException,因此在特定插件的Execute 方法中发生的任何异常都应该使用这个新类重新抛出,如下例所示:
public class PluginExample : ITaskProcessor
{
// ...
public void Execute(Stream sourceStream, Stream destStream)
{
try
{
// ...
}
catch (SomeSpecificException e)
{
throw new TaskProcessingException(e.Message, e);
}
finally
{
// ...
}
}
}
因此,我还应该将以下代码添加到TaskProcessorContext 类的Execute 方法中,以便从插件中捕获TaskProcessingException:
catch(TaskProcessingException e)
{
throw new TaskProcessingException(e.Message, e);
}
这样,TaskProcessorContext 类的用户将能够捕获和管理在调用其Execute 方法期间可能发生的所有异常。
上述方法正确吗? 有其他方法吗?
更新
我修改了TaskProcessorContext.Execute方法,让ITaskProcessor接口的特定实现抛出的异常在自定义的TaskProcessingException中重新启动,这样调用者就可以知道异常发生在哪里并正确处理。
public void Execute(string sourceFileName, string destFileName)
{
FileStream sourceStream = null;
FileStream targetStream = null;
try
{
sourceStream = File.OpenRead(sourceFileName);
targetStream = new FileStream(destFileName, FileMode.Create, FileAccess.Write);
try
{
m_TaskProcessor.Execute(sourceStream, targetStream);
}
catch (Exception e)
{
// Catch and re-throw the exceptions
// launched from a specific plug-in.
throw new TaskProcessingException(e.Message, e);
}
}
catch
{
throw;
}
finally
{
if (sourceStream != null)
{
sourceStream.Close();
sourceStream.Dispose();
sourceStream = null;
}
if (targetStream != null)
{
targetStream.Close();
targetStream.Dispose();
targetStream = null;
}
}
}
【问题讨论】:
-
你为什么要捕获所有这些异常并简单地用内部异常重新抛出它们?你有什么用?它只是用另一个异常包装了异常。
-
你想让插件向消费者抛出错误信息吗?如果是,那么您的方法在最初的观察中看起来不错,但如果您不希望这样,那么您需要将诸如 log4net 之类的记录器与此相关联,这将记录错误信息。此外,对于您的上下文对象,我猜它也应该具有错误属性,以便消费者在处理输出之前检查是否有任何错误。
-
@Amit:是的,我希望插件向消费者抛出错误信息。
-
@YuvalItzchakov:我可以直接写
catch { throw; }吗? -
完全没有捕获如何? :) 如果你只扔,那么抓也没有意义
标签: c# .net exception exception-handling