I have an application which reads a specific file format. Additionally, the application supports “plugins” which read alternate file formats, and convert them into the standardized format the tool natively supports.
I would like to define an interface something like this:
/// <summary>
/// Interface for file transformer. Implementers of this interface provide a means to
/// convert a given file into Foo Format.
/// </summary>
public interface IFileTransformerPlugin
{
/// <summary>Gets the name of the transformer to display to the user.</summary>
/// <value>The name of the transformer displayed to the user.</value>
string DisplayName
{
get;
}
/// <summary>Determines if this transformer can handle the given format.</summary>
/// <remarks>
/// This method should return very quickly; reading only a small portion of the
/// input stream. This method is intended to be a rough cut check -- returning true
/// does not necessarily mean that the file will parse successfully on full load.
/// </remarks>
/// <param name="fileContents">The file contents.</param>
/// <returns>
/// true if this transformer can handle the supplied format, false otherwise.
/// </returns>
bool CanHandleLogFormat(Stream fileContents);
/// <summary>Transforms a file into Foo Format.</summary>
/// <param name="inputFile">The input log stream.</param>
/// <param name="outputFile">The output log stream (where the output should be
/// written).</param>
/// <returns>A transformation result which includes error information.</returns>
LogTransformationResult Transform(Stream inputFile, Stream outputFile);
}
The problem comes from the transform method taking streams. Conceptually, I would like the plugin host, rather than the plugin, to “own” these streams, and be responsible for calling IDispose or whatever on these streams. For instance, under testing scenarios it would be nice for the caller to be able to pass a MemoryStream as the output, and then verify that the output is valid.
However, as a plugin author, it is desirable to be able to use the upper level formatting constructs in the framework; namely TextReader/TextWriter; XmlTextReader/XmlTextWriter; etc. But these classes take ownership of the underlying stream and call Dispose on the underlying stream, regardless of what the code providing the stream does. (At least, assuming those classes themselves are disposed correctly)
How might I rework this interface to get around this problem? Is it even a solvable problem?
This invokes the classic software engineering line: “every problem can be solved with an extra level of indirection”. Just wrap the stream and provide one-liner methods that delegate to the base stream. Except for the Dispose method, do nothing so that whatever the client code does, you keep control of the stream. Like:
Do note the other side of that medal: you are handing a reference to other code. It can store it, you’ll have no idea when they are done with it.