Friday, August 8, 2014

Extend your reach with .Net Extension methods

Got a legacy code to fix/enhance and worried of breaking something existing - Extension methods are your life savers


Recently heard of something like this but at first I actually thought of it more of another standard practice for maintainability and standard but when seen in action? ouuhhh...that will be cool !!

So extension methods enable you to add new functionality to existing types without compiling or modifying existing code. They are written as static methods but called as instance types. Sounds weird right but as complicated they sound, they are far easier to implement. So let's not smug more about it and see them in action. I will come back again on this for what can or cannot be done with Extension methods.

Consider I have an existing DLL which read and write your message on Azure Storage. The storage manager implements an interface IStorageManager which exposes two methods, GetMessage and PutMessage as shown below:-

//Your old code Interface
namespace ExtensionMethodInterface
{
    using System;
    public interface IStorageManager
    {
        void GetMessage();
        void PutMessage();
    }
}

Now there is no method exposed by the interface for Deleting a message from Storage Queue. You might not have the access to the source code of the DLL and may not wish to change it but you still need the delete functionality. You got the right ingredients for .net Extension methods.

So lets create a static class Extension and write our delete message functionality there as shown below:-

/Define an extension
namespace Extensions
{
    using System;
    using ExtensionMethodInterface;

    public static class Extension
    {
        //New extended method for deleting the message from storage
        public static void DeleteMessage(this IStorageManager legacyInterface, int messageId)
        {
            Console.WriteLine("Deleting the message using message id");
        }

        //New extended method for deleting the message from storage
        public static void DeleteMessage(this IStorageManager legacyInterface, string messageCode)
        {
            Console.WriteLine("Deleting the message using message code");
        }

        //This method will never get called becuase each class implementing IStorageManager will have its own implementation
        public static void GetMessage(this IStorageManager legacyInterface)
        {
            Console.WriteLine("Get message from storage");
        }

        //This method will never get called becuase each class implementing IStorageManager will have its own implementation
        public static void PutMessage(this IStorageManager legacyInterface)
        {
            Console.WriteLine("Put message from storage");
        }
    }
}

The above class expose another method DeleteMessage via either Id or Code. Pay attention to the signature of the method. The first argument is the interface type. Now we do have both Get and Put message implementation in this class but these methods will never be called as the original implementation of IStorageManager will have the implementation of these methods. Lets see the original implementation of IStorageManager as below:-

namespace InterfaceImplementation
{   
    using Extensions;
    using ExtensionMethodInterface;

    public class StorageManager : IStorageManager
    {
        //Must implement the GetMessage method
        public void GetMessage()
        {
            Console.WriteLine("Returns the message from Storage");
        }

        public void PutMessage()
        {
            Console.WriteLine("Writes the message on Storage");
        }
    }
}

Typical, implementing the Interface methods for read and write message on Storage Queue. No sign of DeleteMessage method as not exposed by the interface. Now, lets see the class in action:-

namespace ExtensionUsage
{  
    using Extensions;
    using InterfaceImplementation;

    class Usage
    {
        public void UseMyExtension()
        {
            StorageManager manager = new StorageManager();

            //This will be resolved from StorageManager class with the implementation
            manager.PutMessage();
            manager.GetMessage();

            manager.DeleteMessage(100);
            manager.DeleteMessage("code");
        }
    }
}

Now there are couple of things to notice above. We created an instance of the implementation class of IStorageManager interface. We already have the Get and Put message methods implementation in Storage manager class but DeleteMessage is neither present in IStorageManager interface and nor in its implementation in StorageManager. So this call will be resolved from the DeleteMessage method in Extension class.  Great right!!
You never changed your original interface and neither its implementation but still the call resolved from instance of interface implementation and that too as an instance method call and not like static. 

Now, its not exactly a down side but Extension method are never created to override the original code or functionality and hence the extension methods are never called if the have the same signature as the original method in the interface. Also, When using an extension method to extend a type whose source code you cannot change, you run the risk that a change in the implementation of the type will cause your extension method to break.

So choose wisely when you have to use Extension method but if you have to then they are pretty simple to implement. So have fun !!

No comments :

Post a Comment