Extension Methods

.Extension Methods
Extension Method vs Helper Class | Extension Method for Primitives | Extension Method Notes and Guidelines | Extension Method for Interfaces

"Extension methods should be used conservatively and should not be preferred over the traditional ways of achieving object oriented design (i.e. inheritance, interfaces, overloading)."

Extension methods were introduced in .NET 3.5 (C# 3.0) primarily for the support of Language Integrated Query (LINQ). Extension methods allow the integration of LINQ with old and new code. Extension methods allow new capabilities such as being able to extend sealed classes and primitive data types. Extension methods also allow the extension of interfaces with defined implementations. While normally interfaces are not allowed to specify the implementation of any of its members. Extension methods are useful in situations where a new method needs to be added to an existing framework class or a third party library. Extension methods also enable design patterns which are useful in their own right. However, extension methods should be used conservatively and should not be preferred over the traditional ways of achieving object oriented design (i.e. inheritance, interfaces, overloading).

Extension methods can be declared on a class, struct, or an interface. Extension Methods are declared as:

  1. Inside a static class. The class can not be generic or nested.
  2. Inside a static method.
  3. The first method parameter must be the this keyword followed by the type you are extending.

Extension methods differ from static helper methods in the way they are invoked. Extension methods use infix notation while static methods use prefix notation:

Extension Method (infix notation):  Console.WriteLine(myString.ReverseExtensionMethod(1, 4));

Static Helper Class (prefix notation):  Console.WriteLine(HelperClass.ReverseHelperMethod(myString, 1, 4));




Extension Method vs Helper Class

The following program compares an Extension Method with a Helper Class. The extension method and the method in the helper class both perform the same logic, but they are called differently (infix vs prefix). Note the String class is sealed so you can not use inheritance to extend the class.

using System;
namespace HelperClassVsExtensionMethod
{
    // Helper Class (Prefix Notation)
    class HelperClass
    {
        public static string ReverseHelperMethod(string s, int startIndex, int length)
        {
            char[] charArray = s.ToCharArray(startIndex, length);
            Array.Reverse(charArray);
            return new string(charArray);
        }
    }

    // Extension Method (Infix Notation)
    public static class MyExtensionMethods
    {
        public static string ReverseExtensionMethod(this string s, int startIndex, int length)
        {
            char[] charArray = s.ToCharArray(startIndex, length);
            Array.Reverse(charArray);
            return new string(charArray);
        }
    }

    class Program
    {
        static void Main()
        {
            string myString = "ABCDEF";
            Console.WriteLine(HelperClass.ReverseHelperMethod(myString, 1, 4)); // Prints: EDCB
            Console.WriteLine(myString);
            Console.WriteLine(myString.ReverseExtensionMethod(1, 4)); // Prints: EDCB

        }
    }
}

Top



Extension Method for Primitives

Extension methods allow value types (such as the primitives: int, double, float, etc.) to be extended. The following program extends the integer type with a method that performs a mathematical square operation.

namespace PrimitiveExtensionMethod
{
    // Extension Method for Integer Type
    public static class MyExtensionMethods
    {
        public static int Square(this int x)
        {
            return x * x;
        }
    }
   
class
Program
    {
        static void Main()
        {
            int theInt = 8;
            System.Console.WriteLine(theInt.Square()); // Prints: 64
        }
    }
}

Top



Extension Method Notes and Guidelines

Some additional notes and guidelines for using extension methods include:

  1. It is recommended that extension methods be used sparingly, and as a last resort. Whenever possible a type should be extended using inheritance to create a derived type containing the extension, instead of using an extension method.

  2. Extension methods have lower priority than instance methods defined in the type itself. That is, an extension method will never be called if it has the same signature as a method defined in the type.

  3. It is recommended to put extension methods in their own namespaces. That way users will have to explicitly include the namespace to use them. This can also prevent the extension methods from appearing in IntelliSense when they are not being used.

Top




Extension Method for Interfaces




.Extension Method for Interfaces




Extension Method for Interfaces - Program Output

Normally interfaces contain only declarations with no implementation. That is, interfaces only contain the member signatures. However when extension methods are used to extend interfaces, the extension methods must also contain the implementation. This program illustrates the following:

  1. Extending an interface with an extension method.
  2. Interfaces, and their extended methods, are inherited from the base class.
  3. Implementing multiple interfaces in a class.

using System;
namespace InterfaceExtended
{
    // Extension Method of IReaction Interface
    public static class MyExtensionMethods
    {
        public static void ScaredReaction(this IReaction reaction)
        {
            Console.WriteLine("{0}: Run, run, run.", reaction);
        }
    }

    // Noise Interface
    public interface INoise
    {
        void HappyNoise();
        void AngryNoise(int noiseLevel);
    }

    // Reaction Interface
    public interface IReaction
    {
        void HappyReaction();
        void AngryReaction();
    }

    // Base Animal Class
    public class Animal
    {
        public string Name { get; set; }
        public int Mood { get; set; }
        public Animal() { Name = ""; Mood = 0; }
    }

    // Dog - inherits from Animal and implements INoise and IReaction
    public class Dog : Animal, INoise, IReaction
    {
        public void HappyNoise()
        {
            Console.WriteLine("{0}: Arf", Name);
            Mood = 1;
        }
        public void AngryNoise(int noiseLevel)
        {
            string angryNoise = "Grr";
            for (int i = 0; i < noiseLevel; i++)
                angryNoise += "r";
            Console.WriteLine("{0}: {1}", Name, angryNoise);
            Mood = noiseLevel / 2;
        }
        public void HappyReaction()
        {
            Console.WriteLine("{0}: Wag wag Wag", Name);
        }

        public void AngryReaction()
        {
            Console.WriteLine("{0}: Bite - Ouch", Name);
        }
    }

    // Cat - inherits from Animal and implements INoise and IReaction
    public class Cat : Animal, INoise, IReaction
    {
        public void HappyNoise()
        {
            System.Console.WriteLine("{0}: Meow", Name);
            Mood = 1;
        }
        public void AngryNoise(int noiseLevel)
        {
            string angryNoise = "Hiss";
            for (int i = 0; i < noiseLevel; i++)
                angryNoise += "s";
            System.Console.WriteLine("{0}: {1}", Name, angryNoise);
            Mood = noiseLevel / 2;
        }

        public void HappyReaction()
        {
            Console.WriteLine("{0}: Pur pur pur", Name);
        }

        public void AngryReaction()
        {
            Console.WriteLine("{0}: Scratch - Ouch", Name);
        }
    }

    class Program
    {

        static void Header()
        {
            Console.WriteLine("************************************************************");
            Console.WriteLine("***      Interface Example: Dogs and Cats                ***");
            Console.WriteLine("***                                                      ***");
            Console.WriteLine("***  1. Class implements multiple (two) interfaces.      ***");
            Console.WriteLine("***  2. Interfaces are inherited along with base class.  ***");
            Console.WriteLine("***  3. An extension method extends an interface.        ***");
            Console.WriteLine("************************************************************\n");
        }

        static void Main()
        {
            Header();

            Dog Holly = new Dog() { Name = "Holly" };
            Cat Shadow = new Cat() { Name = "Shadow" };
            Holly.HappyNoise();
            Shadow.HappyNoise();
            System.Console.WriteLine();

            // Exetended Interface
            Holly.ScaredReaction();

            Holly.AngryNoise(4);
            Shadow.AngryNoise(6);
            Console.WriteLine();

            switch (Holly.Mood)
            {
                case 3:
                    Console.WriteLine("{0}: I am getting angry.", Holly.Name);
                    break;
                case 2:
                    Console.WriteLine("{0}: I do not like that.", Holly.Name);
                    break;
                default:
                    if (Holly.Mood > 3)
                        Holly.AngryReaction();
                    else
                        Holly.HappyReaction();
                    break;
            }

            switch (Shadow.Mood)
            {
                case 2:
                    Console.WriteLine("{0}: I am getting angry.", Shadow.Name);
                    break;
                case 1:
                    Console.WriteLine("{0}: I do not like that.", Shadow.Name);
                    break;
                default:
                    if (Shadow.Mood > 2)
                        Shadow.AngryReaction();
                    else
                        Shadow.HappyReaction();
                    break;
            }
            Console.WriteLine();
        }
    }
}