Showing posts with label Extension Methods. Show all posts
Showing posts with label Extension Methods. Show all posts

Tuesday, 8 September 2009

IsIn Extension Method

Another useful extension method I've found is the IsIn() method. This is comes in handy when you have to test for enumeration values, for example:
public enum StatusValues
{
 Open,
 Closed,
 Pending,
 Cancelled,
 Expired,
 Suspended
}

   ...

if(myObject.Status == StatusValues.Closed ||
   myObject.Status == StatusValues.Pending ||
   myObject.Status == StatusValues.Expired)
{  
   ...
}

This can become cumbersome when testing for a number of values.

The IsIn Extension Method simplifies this by taking a param list of values.
public static bool IsIn<T>(this T thisObject, params T[] values)
{
    if (thisObject != null && values != null && values.Length > 0)
    {
        foreach (var value in values)
        {
            if (thisObject.Equals(value))
            {
                return true;
            }
        }
    }

    return false;
}
Using this method, the above code can be rewritten as follows:

//using the IsIn Extension Method
if(myObject.Status.IsIn(StatusValues.Closed,
                        StatusValues.Pending,
                        StatusValues.Expired))
{
   ...
}

Monday, 24 August 2009

StringBuilder Extensions

I recently had a requirement to modify some Html-based reports.  These reports were built up in code using a StringBuilder, along with report content data.  The plus-point is that it worked and delivered the desired reports.  On the downside, the code was tedious and verbose.  For example:
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("<TABLE id=\"Table2\" width=\"100%\">");
stringBuilder.Append("<TR style=\"page-break-inside : avoid\"><TD class=\"reportHeaderTitle\" >" + ResourceManager.GetString("ReportTitle", culture) + "</TD></TR>");
stringBuilder.Append("<TR><TD style=\"PADDING-LEFT: 5px; PADDING-BOTTOM:10px\" width=\"100%\">");
stringBuilder.Append("<TABLE id=\"Table2\" width=\"100%\">");
stringBuilder.Append("<TR style=\"page-break-inside : avoid\"><TD class='reportHeader' align=\"center\" width=\"50%\">" + ResourceManager.GetString("ReportSubTitle", culture) + "</TD>");
stringBuilder.Append("<TD class='reportHeader' align=\"center\" width=\"50%\">" + ResourceManager.GetString("ReportResults", culture) + "</TD></TR>");
...

I was staring at 1000s of lines of code like this.  After a while it was all a blur.

My first impulse was to subclass the StringBuilder and build up some Html-specific methods.  Unfortunately, the StringBuilder class is sealed.   I also looked at the HtmlTextWriter class, a class primarily used when working with custom server controls.  But, this seemed overkill for the purpose.  I just needed a clean way to build-up an Html string in code.

I then turned to writing a set of Extensions Methods to provide this functionality.
#region Using Directives

using System.Text;

#endregion

namespace Common.Extensions
{
    public static class StringBuilderExtensions
    {
        public static void BeginTable(this StringBuilder stringBuilder)
        {
            stringBuilder.Append("<table>");
        }

        public static void EndTable(this StringBuilder stringBuilder)
        {
            stringBuilder.Append("</table>");
        }

        public static void BeginRow(this StringBuilder stringBuilder)
        {
            stringBuilder.Append("<tr>");
        }
        ...
    }
}

In refactoring the code I first pulled all of the inline styles out and into style classes.  Then I began migrating the existing code over to use the new extension methods.  The refactored code looks something like this:

var stringBuilder = new StringBuilder();
stringBuilder.HorizontalRule();
stringBuilder.BeginH2();
stringBuilder.Append(ResourceManager.GetString("ReportTitle", _Culture));
stringBuilder.EndH2();

stringBuilder.BeginTable();
stringBuilder.BeginTHead();
stringBuilder.BeginRow();
stringBuilder.BeginCell("reportHeader");
stringBuilder.Append(ResourceManager.GetString("ReportSubTitle", _Culture));
stringBuilder.EndCell();
stringBuilder.BeginCell();
stringBuilder.Append(ResourceManager.GetString("ReportResults", _Culture));
stringBuilder.EndCell();
stringBuilder.EndRow();
stringBuilder.EndTHead();
...
In it's current state, the extensions only supply the Html tags that were required, but could easily be expanded to support all of the commonly used tags.  I personally find this refactored code much easier to read and maintain.

You can download the StringBuilderExtensions.zip here.
I hope this may be of some use.

Extension Method Usage

If you are like me, once you have the Big Extension Method Realisation you tend to go overboard on their use.  This is eventually followed by the more practical Extension Method Usage Realisation.  This second realisation puts Extension Methods into perspective.

For example, you could quite easily create an extension method like this:
public static bool IsNull(this object thisObject)
{
return thisObject == null;
}
This method can replace
if(myObject==null)

with...

if(myObject.IsNull())

But is it worth it?  Is there anything wrong with myObject==null?  Of course not.  So why change it or replace it with an extension method.  It adds no new functionality, just a change of syntax.
Additionally, you will often collect your extension methods and place them in a common assembly.  Use of the above extension method creates an dependency on this assembly.  But what does it buy you?  I would argue, nothing.  So, lose it.
From my experience in working with extension methods, I've found the most valuable to be methods that supply missing functionality or ones that encapsulate often repeated code. 
An example of this is checking if a collection contains any items, kind of like a cousin of String.IsNullOrEmpty.  For me, this is a prime candidate for an extension method, and should have been included in the IList interface (IMHO).

public static bool HasItems(this IList collection)
{
return collection != null && collection.Count > 0;
}
So we reduce
if(myCollection!=null && myCollection.Count>0)

to a clean and simple

if(myCollection.HasItems())

I know there is not a shortage of resources on the web regarding extension methods.  However, after working with them for a few years, I have managed to collect a few "must have" extension methods, which I will be posting over time.