Friday, May 7, 2010

Implementing "Extension Methods" for Reversing Strings in C#

While working with strings in C#, to my surprise I couldn't find any inbuilt function to reverse a string. I decided to take on this trivial task.


Before starting out, I was curious to know what really are strings made up of? In .Net, the System.String class is an array of System.Char datatypes, which could hold Unicode values. The string class can hold values which is only limited by the amount of physical memory available.


What is an Extension Method?
Before we move on, If the term "Extension Methods" sounds new to you; then here is concise definition. The "Extension Methods" provides a way to extend existing inbuilt .Net classes, with you own set of methods without sub-classing or recompiling the types. You can accesses your extension methods just like any other methods defined by the type, with the only limitation that you won't be able to access the member variables or internal methods defined inside the type. This feature was introduced with C# version 3.0. If you want know more about extension methods visit MSDN.


What really are Extension Methods at its Core?
The Extension Methods are normal static methods, decorated with the "[Extension]" attribute, by the compiler. If you want to explore further, visit Scott hanselman's blog


So let me show you my very first string reversing extension method.


public static class Utils
{
    public static string Reverse(this string Val)
    {
        bool Proceed;
        
        Proceed = (Val != null);
        Proceed = Proceed && (Val.Length > 1);

        if (Proceed)
        {            
            char[] Chars = Val.ToCharArray();
            Array.Reverse(Chars);
            Val = new string(Chars);
        }

        return Val;
    }
}

Here in the above code snippet, you could see I am using the "Array.Reverse()" method to reverse the character array and passing back as string. 


To my satisfaction, i thought of writing my own reverse function using loops, i.e; WITHOUT using the Array.Reverse() method. 


public static class Utils
{
    public static string Reverse(this string Val)
    {
        bool Proceed;

        Proceed = (Val != null);
        Proceed = Proceed && (Val.Length > 1);

        if (Proceed)
        {
            char[] Chars = Val.ToCharArray();

            for (int i = 0; i < Chars.Length / 2; i++)
            {
                char a = Chars[Val.Length - i - 1];
                Chars[Val.Length - i - 1] = Chars[i];
                Chars[i] = a;
            }

            Val = new string(Chars);
        }

        return Val;
    }
}

The only difference between the first code snippet over this one is that, there is a "For" loop that loops through half of the given characters and does a character swap, pretty simple.


After having two Reverse functions, i got myself in a dilemma on which one to use. There came the idea to evaluate performance aspect of both the methods. I did a performance benchmark by  passing huge string values to both the extension methods and logged the time it took to reverse a string. From the results you could see that, the custom string reverse function, i.e; the one using "For" loop, is clearly the winner while dealing with huge strings.


If you want to try out the benchmarking test for yourself. I am posting the code, which i had used to benchmark the string reverse methods. Please note, while executing the below code snippet, might throw an OutOfMemory exception on machines with low RAM. I had executed this code on a system with 4 GB RAM, on which everything went perfectly fine.

class Program
{
    static void Main(string[] args)
    {
        string str1 = "abcdefgijklmnopqrstuvwxyz";
        string str2 = "abcdefgijklmnopqrstuvwxyz";
        DateTime DT1Start, DT1End;
        DateTime DT2Start, DT2End;
        TimeSpan DT1, DT2;

        for (int i = 0; i < 22; i++)
        {
            str1 += str1;
            str2 += str2;
        }

        Console.WriteLine("CustomReverse Array.Reverse");

        for (int i = 0; i < 25; i++)
        {

            DT2Start = DateTime.Now;
            str2.Reverse1(); //Array.Reverse
            DT2End = DateTime.Now;
            DT2 = DT2End.Subtract(DT2Start);

            DT1Start = DateTime.Now;
            str1.Reverse(); //Custom Reverse
            DT1End = DateTime.Now;
            DT1 = DT1End.Subtract(DT1Start);


            Console.Write(DT1.Milliseconds.ToString().PadLeft(6) + " ms");
            Console.WriteLine(DT2.Milliseconds.ToString().PadLeft(13) + " ms");

        }
    }
}

public static class Utils
{
    public static string Reverse(this string Val)
    {
        bool Proceed;

        Proceed = (Val != null);
        Proceed = Proceed && (Val.Length > 1);

        if (Proceed)
        {
            char[] Chars = Val.ToCharArray();

            for (int i = 0; i < Chars.Length / 2; i++)
            {
                char a = Chars[Val.Length - i - 1];
                Chars[Val.Length - i - 1] = Chars[i];
                Chars[i] = a;
            }

            Val = new string(Chars);
        }

        return Val;
    }

    public static string Reverse1(this string Val)
    {
        bool Proceed;

        Proceed = (Val != null);
        Proceed = Proceed && (Val.Length > 1);

        if (Proceed)
        {
            char[] Chars = Val.ToCharArray();
            Array.Reverse(Chars);
            Val = new string(Chars);
        }
        return Val;
    }

}

I really recommend using the custom string reverse method, i.e; the one using "For" loop. If you happen to encounter any issues, please drop me a comment.



No comments: