Friday, January 25, 2008

Stop subclassing and using Helpers - start Extension Methods

How many classes, named Helper, you have on your hard disk? I counted, and I have 320. What is "Helper"? Helper - is static class, that consists of static methods, that provide simple services to your program. Good examples for such methods are

public static string FormatTime(TimeSpan time)
        {
            return time.Hours.ToString("00") + ":" + time.Minutes.ToString("00") + ":" + time.Seconds.ToString("00");
        }

or

public static byte[] ConvertObjectToByteArray(object obj)
        {
            byte[] _resp = null;
            BinaryFormatter formatter = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                formatter.Serialize(ms, obj);
                _resp = ms.ToArray();
            }

            return _resp;
        }

or, even

public static byte[] Compress(byte[] buffer)
       {
           MemoryStream ms = new MemoryStream();
           GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true);
           zip.Write(buffer, 0, buffer.Length);
           zip.Close();
           ms.Position = 0;

           MemoryStream outStream = new MemoryStream();

           byte[] compressed = new byte[ms.Length];
           ms.Read(compressed, 0, compressed.Length);

           byte[] gzBuffer = new byte[compressed.Length + 4];
           Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
           Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
           return gzBuffer;
       }

Those stand-alone methods are in use all over the application and provide small, frequent services.

What are disadvantages of such approach?

  • Another class = wider maintenance
  • You never know where it in use (even after searching)
  • You should remember all services provided
  • Sometimes, you have a number of overloads for those methods in order to make it's using simpler.
  • If you are working on shared project, someone can harm all your project, by changing "something small" in Helper class.

Can we do something else? Yes, we can. We can subclass our objects and hide those methods inside it. This approach is even worse. In spite of easiness of intellisense, provided with this method, you'll have large number of almost the same classes with little changes. Also, we cannot do anything with sealed classes.

What to do? In C# 3.0 (not only WPF), Extension Methods were introduced. What is it? The same "Helper", with only one difference - this keyword before the first argument. So, when changing our code to following:

public static string FormatTime(this TimeSpan time)
        {
            return time.Hours.ToString("00") + ":" + time.Minutes.ToString("00") + ":" + time.Seconds.ToString("00");
        }

or

public static object ConvertByteArrayToObject(this byte[] data)
        {
            object[] _resp = null;

            BinaryFormatter formatter = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream(data, 0, data.Length))
            {
                try
                {
                    object _tmp = formatter.Deserialize(ms);
                    _resp = _tmp as object[];
                }
                catch
                {
                    _resp = new object[1];
                }
            }

            return _resp;
        }

or, even

public static byte[] Decompress(this byte[] gzBuffer)
        {
            MemoryStream ms = new MemoryStream();
            int msgLength = BitConverter.ToInt32(gzBuffer, 0);
            ms.Write(gzBuffer, 4, gzBuffer.Length - 4);

            byte[] buffer = new byte[msgLength];

            ms.Position = 0;
            GZipStream zip = new GZipStream(ms, CompressionMode.Decompress);
            zip.Read(buffer, 0, buffer.Length);

            return buffer;
        }

We will able to use all those methods within regular classes of argument type. Full intellisense support and very easy syntax. Like this.

image

But, do not go to extreme with extension methods, 'cos you likely face with the problem, exists now with arrays and Linq - too much extensions. You'll never know what are the original class methods (except icons :)). See yourself

image

Have a nice day. I had not time (as always) to review this post before publishing :)

No comments: