Extension methods, Advantages, Best Practices

18. May 2011

 

What is Extension Method?

Extension methods allow developers to add new methods to the public contract of an existing type, without having to sub-class it or recompile the original type. It allows static methods to be invoked on instance variables. Extension methods can be defined on classes, structures and interfaces.

These methods must take at least one parameter, which represents the instance the method is to operate on. For example, in C#, this is done by using the modifier on such a parameter, when defining the method: 

public static bool IsPalindrome(this string s)
{
      //implementation follows here
}

This example allows one to write, for example:

“some string”.IsPalindrome();

 

Advantages:

  • Cleaner and readable code. LINQ benefits greatly from extension methods as LINQ queries would be almost unreadable without them.
  • Extend functionality on third party objects and sealed classes.
  • Intellisense support. When you add an extension method M on type T, you get 'M' in T's intellisense list (assuming the extension class is in-scope). This make 'M' much easier to find than StaticClass.M(T,...)
  • Create default functionality for interfaces without having to implement an abstract class

 

Disadvantage:

  • There's no compiler error or warning if you have a regular method and an extension method with the same signature in the context.
  • Object and the extension method are versioned independently. You can be put in a trouble when vendor of a class you are extending on creates the method with the same name.
  • You have an extension method and another library creates an extension method with the same signature? You will end up with difficulties in using both namespaces.
  • You can only have extension methods, not properties, indexers, operators, constructors etc.

 

Best practice:

  • Put extension methods into their own namespace. By putting extension methods into their own namespace you enable consumers to include or exclude them separately from the rest of your library. This makes them pluggable.
  • Avoid defining extension methods on System.Object, unless absolutely necessary. When doing so, be aware that VB users will not be able to use thus-defined extension.
  • Think twice before extending types you don't own. The types can include methods with the same name any time.
  • Avoid redefining extension methods on a type T with extension methods on the same type.
  • Use extension methods :
    • To provide helper functionality relevant to every implementation of an interface. 
    • To provide extension to a library that cannot be extended in other way and on which you do not have access over
    • To use with Interfaces. Interfaces cannot include behavior by default. But now you can add it with Extension Methods

 

.Net, c# , ,

Useful Extension Methods - Part 1 of n

28. August 2010

    Extension methods let you add new features to existing classes, even classes that you didn't write yourself. You can use them to add new methods to your own types, types written by others including basic data types.

In this series, I Will be posting some useful extension methods for the all the types. To begin with I am providing extension methods for strings.

String Extension Methods -1:

IsValidUrl

Checks if the string is a valid URL

public static bool IsValidUrl(this string input) 

{

    Regex rx = new Regex(@"http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?");

    return rx.IsMatch(input);

}

//Usage

"http://elangovanr.com".IsValidUrl();

 

IsValidEmailAddress

Checks if the input string is a valid email address

public static bool IsValidEmailAddress(this string input)

{

    Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");

    return regex.IsMatch(input);

}

 

//Usage

"admin@elangovanr.com".IsValidEmailAddress();

 

IsNotNullOrEmpty

Checks if the input is not null or empty, negation of IsNullOrEmpty

public static bool IsNotNullOrEmpty(this string input) 

{

    return !String.IsNullOrEmpty(input);

}

 

//Usage

"test".IsNotNullOrEmpty();

 

ToBase64

Converts the string to base 64 encoded string

static public string ToBase64(this string input)

{

    byte[] toEncodeAsBytes

        = System.Text.ASCIIEncoding.ASCII.GetBytes(input);

    string returnValue

        = System.Convert.ToBase64String(toEncodeAsBytes);

    return returnValue;

}

 

//Usage

"test".ToBase64();

 

FromBase64

Decodes base 64 encoded string

static public string FromBase64(string input)

{

    byte[] encodedDataAsBytes

      = System.Convert.FromBase64String(input);

    string returnValue =

    System.Text.ASCIIEncoding.ASCII.GetString(encodedDataAsBytes);

    return returnValue;

}

 

//Usage

"test".FromBase64();

 

IsValidIPAddress

Checks if the given string is a valid IP Address

public static bool IsValidIPAddress(this string s)

{

    return Regex.IsMatch(s, 

            @"\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b");

}

 

//Usage

"test".IsValidIPAddress();

 

ToHTMLEncoded

Encodes the input string as HTML (converts special characters to entities)

public static string ToHTMLEncoded(this string input)

{

    return HttpContext.Current.Server.HtmlEncode(input);

}

 

//Usage

"<span>test</span>".ToHTMLEncoded();

 

ToURLEncoded

Encodes the input string as a URL (converts special characters to % codes)

public static string ToURLEncoded(this string input)

{

    return HttpContext.Current.Server.UrlEncode(input);

}

 

//Usage

"http://elangovanr.com/?q=extension&page=2".ToURLEncoded();

 

HTMLDecoded

Decodes the HTML Encoded string

public static string HTMLDecoded(this string input)

{

    return HttpContext.Current.Server.HtmlDecode(input);

}

 

//Usage

"test".HTMLDecoded();

 

URLDecoded

Decodes the HTML Encoded string

public static string URLDecoded(this string input)

{

    return HttpContext.Current.Server.UrlDecode(input);

}

 

//Usage

"test".URLDecoded();

 

StripHTML

Removes any HTML tags from the input string

public static string StripHTML(this string input)

{

    return Regex.Replace(input, @"<(style|script)[^<>]*>.*?</\1>|</?[a-z][a-z0-9]*[^<>]*>|<!--.*?-->", "");

}

 

//Usage

"<span>test</span>".URLDecoded();

.Net Tips, Technical, c# ,

Catch multiple Exceptions together in C#

27. August 2010

    It is discouraged to simply catch System.Exception, instead only the "known" Exceptions should be caught. Now, this sometimes leads to unneccessary repetetive code, for example:

            try

            {

                DoSomething();

            }

            catch (FormatException ex)

            {

                LogException(ex);

                DisplayMessage(ex.Message);

            }

            catch (OverflowException ex)

            {

                LogException(ex);

                DisplayMessage(ex.Message);

            }

    if want to perform same operation on two different "known" Exceptions, C# does not allow you to catch exceptions like:

catch(FormatException | OverflowException ex)  // may be implemented in future versions of C#...

    But, you can refactor the above code to avoid the code repetitions. You can change the above code like:

            try

            {

                DoSomething();

            }

            catch (Exception ex)

            {

                if (ex is FormatException 

               || ex is OverflowException)

               {

                   LogException(ex);

                   DisplayMessage(ex.Message);

               }

               else

               {

                   throw;

               }

            }            

or, in other way,

            try

            {

                DoSomething();

            }

            catch (Exception ex)

            {

                Type exType = ex.GetType();

               if (exType == typeof(System.FormatException) 

               || exType == typeof(System.OverflowException))

               {

                   LogException(ex);

                   DisplayMessage(ex.Message);

               }

               else

               {

                    throw;

               }

            }            

    You can use the second option only if you want to handle the particular exception and not its derived Exceptions types. C# catch staement catches the Exception and Its derived types, so the best alternative for the original code can be the option 1.

.Net Tips, c# , ,

Using var (Implicitly Typed Local Variables) in your C# code.

26. August 2010

    Local variables can be given an implicit type of 'var' instead of explicit types. The compiler will resolve the type from the expression. Note that var can only be used when a local variable is declared and initialized in the same statement.

    I found some people discouraging the usage of 'var' in C# code and the reason that they are giving is that the var type makes the code more difficult to understand. It is not always true, using the var keyword should be discouraged in a places like the following code:

var data = GetData();

    When you read the above code, we will not know the type of the variable 'data'. This code is not understandable as it can be more it can be. If you change the above code like below, it makes the code more understandable.

DataTable data = GetData();

    'var' can be used in places like where below. <.p>

var data = new DataTable();

Here, the reader will not have any misunderstanding about the type of the variable ‘data’.

It makes sense to use var in teh following places.

  1. When creating anonymous types : var person = new { Name = "Name", Age = 25 };
  2. Object creation : var data = new DataTable();
  3. Cast expression: var data = (DataTable)obj; or var data = obj as DataTable;
  4. Generic method call or property with explicit type arguments, when return type is generic: var manager = serviceProvider.GetService<IManager>() or var manager = Singleton<Manager>.Instance;

.Net Tips, Technical, c# , ,

C# - Conditional Attribute

20. August 2010

 

  If you are working on C#, you probably know about methods like Debug.WriteLine. The important part of their usage is that they only do stuff if you are running an application compiled in Debug mode. If you are running in release mode, it works like those calls in your code don't even exist. When you compile your code  with Debug.WriteLine() method call in debug mode and look at the MSIL, you can see the method call to Debug.WriteLine() and when you compile the same code in the Release mode and look at the MSIL, you cannot find the method call in the MSIL.Have you ever wondered how these methods are implemented? Have you ever wanted to create a method like this? 

    This can be achieved using pre-processor directives in C#, but wait, when you use pre-processor directives, you have to use the pre-processor directives before the method declaration and of course, before the method usage otherwise the compiler will give error. But here, we are not using any pre-processor directives before the method call. How the magic is done? This is simple, if you look at the syntax of the method Debug.WriteLine(), you can find one attribute [Conditional("DEBUG")] before the method.

What is Conditional Attribute?

 

    Conditional Attribute tag on the method signature means that the method call only exists if the specified pre-processor directive is defined. In this case, the method call only exists if the symbol DEBUG is defined.

For example, take a look at the following code:

 class Program
    {
        static void Main(string[] args)
        {
            DebugInfo();
        } 

        [Conditional("DEBUG")]
        public static void DebugInfo()
        {
            Console.WriteLine("This is debug information");
        }
    }

When this code is compiled in debug mode, if you take a look at the MSIL for the Main() method inside the assembly, you will see the following: 

.method private hidebysig static void Main(string[] args) cil managed

{

    .entrypoint

    .maxstack 8

    L_0000: nop 

    L_0001: call void ConditionalAttributeTest.Program::DebugInfo()

    L_0006: nop 

    L_0007: ret 

}

And if you compile the same code in in Release mode and look at the MSIL for the Main() method, you can see the following:

.method private hidebysig static void Main(string[] args) cil managed

{

    .entrypoint

    .maxstack 8

    L_0000: ret 

}

    Here, we cannot see the call to the method DebugInfo(). But we can see the Method DebugInfo present in the MSIL. The Conditional attribute instructs the compiler to remove the method call to this method.

 

Where is this usefull?

 

    For example, you wanted to gather some performance data when the symbol TIMERS is defined. You can have some code like the following:

public static class StopWatches

    {

        private static Dictionary<string, Stopwatch> _stopwatches

      = new Dictionary<string, Stopwatch>();

 

        [Conditional("CHECKPERFORMANCE")]

        public static void StartStopwatch(string key)

        {

            if (_stopwatches.ContainsKey(key))

            { return; }

            _stopwatches.Add(key, Stopwatch.StartNew());

        }

 

        [Conditional("CHECKPERFORMANCE")]

        public static void StopStopwatch(string key)

        {

            if (!_stopwatches.ContainsKey(key))

            { return; }

            var watch = _stopwatches[key];

            watch.Stop();

            _stopwatches.Remove(key);

            Console.WriteLine(String.Format("Timer: {0}, {1}ms", key,

                watch.Elapsed.TotalMilliseconds));

        }

    }

 

    class Program

    {

        static void Main(string[] args)

        {

            StopWatches.StartStopwatch("forloop");

            int total = 0;

            for (int i = 0; i < 10000; i++)

            { total += i; }

            Console.WriteLine(total);

            StopWatches.StopStopwatch("forloop");

 

            Console.Read();

        }

    }

When you run the above code with #define CHECKPERFORMANCE, you will get the following output.

 

49995000

Timer: forloop, 0.9802ms

 

When you run the above code without defining the pre-processor directive CHECKPERFORMANCE, you will see the following output.

49995000


 

.Net Tips, Technical, c# , ,