I have started to use classes implementing IDisposable to write blocks in streams, with the using statement. This is helpful to keep a correct nesting and avoid missing or wrongly placed start/end parts.
Basically, the constructor writes the start of a block (e.g. opening XML tag), Dispose() the end (e.g. closing XML tag). Example is the UsableXmlElement below (it's for large XMLs, so LINQ to XML or XmlDocument in memory are no options).
However, these IDisposable do not implement the sophisticated pattern recommended by Microsoft, with Destructor/Finalizer, separate Dispose(bool) method and GC.SuppressFinalize(). Dispose simply writes the end element, and that's it.
Is there any down side to this, or is this a good way to maintain a correct nesting of elements?
class UsableXmlElement : IDisposable
{
private XmlWriter _xwriter;
public UsableXmlElement(string name, XmlWriter xmlWriter)
{
_xwriter = xmlWriter;
_xwriter.WriteStartElement(name);
}
public void WriteAttribute<T>(string name, T value)
{
_xwriter.WriteStartAttribute(name);
_xwriter.WriteValue(value);
_xwriter.WriteEndAttribute();
}
public void WriteValue<T>(T value)
{
_xwriter.WriteValue(value);
}
public void Dispose()
{
_xwriter.WriteEndElement();
}
}
Usage is like this:
var xWriter = new XmlWriter(...)
using(var rootElement = new UsableXmlElement("RootElement", xWriter)
{
rootElement.WriteAttribute("DocVersion", 123)
using(var innerElement = new UsableXmlElement("InnerElement", xwriter)
{
// write anything inside Inner element
}
}
Resulting in:
<RootElement DocVersion="123">
<InnerElement>
<!-- anything -->
</InnerElement>
</RootElement>
The main downside I see (apart from the non-standard use of the using statement arguably violating the "principle of least surprise") is that it will attempt to repeatedly write all the nested end tags in the event of an exception thrown by your XmlWriter
.
In theory at least, you could have an exception thrown while writing an inner end tag, followed by successful writes of outer end tags in the "finally" blocks generated by the using statements. This would lead to invalid output.
Is there any down side to this,
No. Destructors (finalizers) should be avoided anyway, even a class with resources can usually do (better) without.
or is this a good way to maintain a correct nesting of elements?
Yes. You can use System.Web.Mvc.Html.MvcForm
as a reference.
these IDisposable do not implement the sophisticated pattern recommended by Microsoft
That 'full' pattern is correct but dated. It only describes the situation for a 'bare' unmanaged resource. An official references for dealing with managed resources only is not provided, unforunately.
The complex pattern used by microsoft is created to ensure that unmanaged resources are freed, even if you don't call Dispose()
.
You don't use any unmanaged resources in your classes. You simply take advantage of C#'s using
keyword to make your code more readable and maintainable. I think that's a nice approach and i have also used it in the past.
I think it would not even make sense to use the finalizer construct, since you need to be sure that the end tag gets written at the correct position. You never know when the finalizer is called, so you could not be sure when the end tag would be written if you forget to Dispose your element. Your Xml document would still be messed up, whether the end tag is not written at all or at the wrong position.
In the worst case, when the finalizer calls Dispose(), the XmlWriter could already be disposed and you get an Exception. So better no finalizer.
来源:https://stackoverflow.com/questions/9515486/usingidisposable-obj-new-in-c-sharp-to-write-code-blocks-in-stream-e-g