我正在开发一个插件架构,允许通过使用适当的插件来执行异构任务,插件将从一个或多个库(来自指定目录的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
方法中发生的任何异常,如下例所示: / p>
public class PluginExample : ITaskProcessor
{
// ...
public void Execute(Stream sourceStream, Stream destStream)
{
try
{
// ...
}
catch (SomeSpecificException e)
{
throw new TaskProcessingException(e.Message, e);
}
finally
{
// ...
}
}
}
因此,我还应该将以下代码添加到Execute
类的TaskProcessorContext
方法中,以便从插件中捕获TaskProcessingException
:
catch(TaskProcessingException e)
{
throw new TaskProcessingException(e.Message, e);
}
通过这种方式,TaskProcessorContext
类的用户可以捕获和管理在调用Execute
方法期间可能发生的所有异常。
上述方法是否正确? 有替代方法吗?
更新
我修改了TaskProcessorContext.Execute
方法,以便在自定义的TaskProcessingException中重新启动ITaskProcessor
接口的特定实现抛出的异常,以便调用者可以知道异常发生的位置并正确处理它。 / p>
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;
}
}
}
答案 0 :(得分:1)
恕我直言,你对插件开发人员施加了太大的压力,要求他们只抛出某种类型的异常。
由于没有办法强制执行此行为,即从pugin执行上下文中仅抛出TaskProcessingException
,您和您的类的消费者都不应该依赖它。
另一方面,您可以引入TaskProcessingException
作为插件在执行诸如从流接收的错误数据之类的任务时发出失败预期的方式。在这种情况下,这将被视为业务异常,而TaskExecutionContext
的使用者将能够将其与TaskExecutionContext
抛出的其他异常类型区分开来。