zaterdag 7 maart 2009

Nice XmlWriter extender

Sometimes I just like to write some code. I try to think of something to make usage of framework parts even better. I remembered the burnden of writing and XML file using the XmlWriter class. Loosing track of all the WriteStartXXX and WriteEndXXX in a method can be hard to fix and Visual Studio removing any indenting when reformatting is accidently executed doesn't help.

I saw the Tracer class in the logging code block of the Enterprise Library. This class is supposed to be used in a using() { ... } constuction. The constructor takes care of starting the trace and when the object is disposed ending the trace is taken care of.

With this in mind and using my personal favorite feature: extension methods. I came up with the following:


public static class XmlWriterExtensions {

public abstract class XmlWriterWatcher : IDisposable
{
protected XmlWriter writer;
private bool disposed = false;

public XmlWriterWatcher(XmlWriter writer)
{
this.writer = writer;
}

protected abstract void CallWriteEndMethod();

#region IDisposable Members

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

public void Dispose(bool disposing)
{
if (!this.disposed)
{
// If disposing equals true, dispose all managed and unmanaged resources.
if (disposing)
{
CallWriteEndMethod();
writer = null;
}

// Call the appropriate methods to clean up
// unmanaged resources here.
}
disposed = true;
}

#endregion

~XmlWriterWatcher()
{
Dispose(false);
}
}

public class StartElementWatcher : XmlWriterWatcher
{
private bool useWriteFullElementEnd = false;

public StartElementWatcher(XmlWriter writer)
: base(writer)
{
}

public StartElementWatcher(XmlWriter writer, bool useWriteFullElementEnd)
: base(writer)
{
this.useWriteFullElementEnd = useWriteFullElementEnd;
}

protected override void CallWriteEndMethod()
{
if (useWriteFullElementEnd)
writer.WriteFullEndElement();
else
writer.WriteEndElement();
}

}

public class StartDocumentWatcher : XmlWriterWatcher
{
public StartDocumentWatcher(XmlWriter writer)
: base(writer)
{
}

protected override void CallWriteEndMethod()
{
writer.WriteEndDocument();
}
}

public class StartAttributeWatcher : XmlWriterWatcher
{
public StartAttributeWatcher(XmlWriter writer)
: base(writer)
{
}

protected override void CallWriteEndMethod()
{
writer.WriteEndAttribute();
}
}

public static StartElementWatcher StartElement(this XmlWriter writer, string localName)
{
writer.WriteStartElement(localName);
return new StartElementWatcher(writer);
}

public static StartElementWatcher StartElement(this XmlWriter writer, string localName, string ns)
{
writer.WriteStartElement(localName, ns);
return new StartElementWatcher(writer);
}

public static StartElementWatcher StartElement(this XmlWriter writer, string prefix, string localName, string ns)
{
writer.WriteStartElement(prefix, localName, ns);
return new StartElementWatcher(writer);
}

public static StartElementWatcher StartElement(this XmlWriter writer, string localName, bool useWriteFullEndElement)
{
writer.WriteStartElement(localName);
return new StartElementWatcher(writer, useWriteFullEndElement);
}

public static StartElementWatcher StartElement(this XmlWriter writer, string localName, string ns, bool useWriteFullEndElement)
{
writer.WriteStartElement(localName, ns);
return new StartElementWatcher(writer, useWriteFullEndElement);
}

public static StartElementWatcher StartElement(this XmlWriter writer, string prefix, string localName, string ns, bool useWriteFullEndElement)
{
writer.WriteStartElement(prefix, localName, ns);
return new StartElementWatcher(writer, useWriteFullEndElement);
}

public static StartDocumentWatcher StartDocument(this XmlWriter writer)
{
writer.WriteStartDocument();
return new StartDocumentWatcher(writer);
}

public static StartDocumentWatcher StartElement(this XmlWriter writer, bool standalone)
{
writer.WriteStartDocument(standalone);
return new StartDocumentWatcher(writer);
}

public static StartAttributeWatcher StartAttribute(this XmlWriter writer, string localName)
{
writer.WriteStartAttribute(localName);
return new StartAttributeWatcher(writer);
}

public static StartAttributeWatcher StartAttribute(this XmlWriter writer, string localName, string ns)
{
writer.WriteStartAttribute(localName, ns);
return new StartAttributeWatcher(writer);
}

public static StartAttributeWatcher StartAttribute(this XmlWriter writer, string prefix, string localName, string ns)
{
writer.WriteStartAttribute(prefix, localName, ns);
return new StartAttributeWatcher(writer);
}

}


Usage of this added feature to the XmlWriter would be as follows:


using (var w = XmlWriter.Create("D:\\Text.xml"))
{
using (w.StartDocument())
{
using (w.StartElement("TestElement"))
{
w.WriteString("test123");
}
}
}

zaterdag 24 mei 2008

Making shortcuts for case-insensitive String.IndexOf

Extension methods in C# allow programmers to correct situations where .NET does not provide the easiest way to do things. Most of the times when I want to do a String.IndexOf, it is the case insensitive version I need. .NET defaults to the most sensitive version. In c# the following code is needed to perform such a call:


if (str.IndexOf("test", StringComparison.OrdinalIgnoreCase) >= 0) {
...
}
The extension methods for the IndexOf are called IndexOf_CI. They cannot be called IndexOf since the parameter signature is the same. Where CI stands for Case-Insensitive. Below I created the overloads for IndexOf the framework offers.


public static int IndexOf_CI(this String str, string value)
{
return str.IndexOf(value, StringComparison.OrdinalIgnoreCase);
}

public static int IndexOf_CI(this String str, char value)
{
return str.IndexOf(value.ToString(), StringComparison.OrdinalIgnoreCase);
}

public static int IndexOf_CI(this String str, string value, int startIndex)
{
return str.IndexOf(value, startIndex, StringComparison.OrdinalIgnoreCase);
}

public static int IndexOf_CI(this String str, char value, int startIndex)
{
return str.IndexOf(value.ToString(), startIndex, StringComparison.OrdinalIgnoreCase);
}

public static int IndexOf_CI(this String str, string value, int startIndex, int count)
{
return str.IndexOf(value, startIndex, count, StringComparison.OrdinalIgnoreCase);
}

public static int IndexOf_CI(this String str, char value, int startIndex, int count)
{
return str.IndexOf(value.ToString(), startIndex, count, StringComparison.OrdinalIgnoreCase);
}
The code becomes a lot shorter en more friendly to the eye:


if (str.IndexOf_CI("test") >= 0) {
...
}
To complete this library also the IndexOfAny and LastIndexOf need to be supplied with case-insensitive versions.

A System.String extension method Substring

kick it on DotNetKicks.com

Almost every time I use String.Substring() I have to do a few IndexOf calls to find the "startIndex" en "length" to use. This includes declaring local variables and verifying that the index found is not smaller than zero. This extension method solves these issues. (Long live C# 3.0!)


public static string Substring(this String str, string startText, string endText, StringComparison comparisonType)
{
string lsResult = "";
Int32 liStartPos = str.IndexOf(startText, comparisonType);
if (liStartPos >= 0) {
liStartPos = liStartPos + startText.Length;
Int32 liEndPos = str.IndexOf(endText, liStartPos, comparisonType);
if (liEndPos >= 0) {
lsResult = str.Substring(liStartPos, liEndPos - liStartPos);
}
}
return lsResult;
}

public static string Substring(this String str, string startText, string endText)
{
string lsResult = "";
Int32 liStartPos = str.IndexOf(startText, StringComparison.CurrentCulture);
if (liStartPos >= 0) {
liStartPos = liStartPos + startText.Length;
Int32 liEndPos = str.IndexOf(endText, liStartPos, StringComparison.CurrentCulture);
if (liEndPos >= 0) {
lsResult = str.Substring(liStartPos, liEndPos - liStartPos);
}
}
return lsResult;
}
I wasn't sure if overloading existing methods was alowed, but it works.

kick it on DotNetKicks.com