Extension methods

Extension methods are static methods that can be invoked using the instance method syntax. In effect, extension methods make it possible for us to extend existing types and constructed types with additional methods.

For example, we can define an extension method as follows:

    public static class MyExtensions
    {
        public static bool IsCandy(this Product p)
        {
            if (p.ProductName.IndexOf("candy") >= 0)
                return true;
            else
                return false;
        }
    }

In this example, the static method IsCandy takes a this parameter of the Product type and searches for the string candy inside the product name. If it finds a match, it assumes this is a candy product and returns true. Otherwise, it returns false, meaning this is not a candy product.

Because all extension methods must be defined in top-level static classes, here to simplify the example, we put this class inside the same namespace as our main test application TestLINQFeaturesApp, and defined this class on the same level as the Program class so that it is a top-level class. Now, in the program, we can call this extension method as follows:

if (product.IsCandy())
    Console.WriteLine("yes, it is a candy");
else
    Console.WriteLine("no, it is not a candy");

It looks as if IsCandy is a real instance method of the Product class. Actually, it is a real method of the Product class, but it is not defined inside the Product class. Instead, it is defined in another static class to extend the functionality of the Product class. This is why it is called an extension method.

Not only does it look like a real instance method, but this new extension method actually pops up when a dot is typed by using IntelliSense on the product variable. The following screenshot shows the possible options when using IntelliSense on the product variable within Visual Studio:

Extension methods

Under the hood in Visual Studio, when a method call on an instance is being compiled, the compiler first checks to see if there is an instance method in the class for this method. If there is no matching instance method, it looks for an imported static class or any static class within the same namespace. It also searches for an extension method with the first parameter that is the same as the instance type (or is a super type of the instance type). If it finds a match, the compiler will call that extension method. This means that instance methods take precedence over extension methods, and extension methods that are imported in inner namespace declarations take precedence over extension methods that are imported in outer namespaces.

In our example, when product.IsCandy() is being compiled, the compiler first checks the Product class and doesn't find a method named IsCandy. It then searches the static class MyExtensions and finds an extension method with the name IsCandy, and due to that it has a first parameter of the type Product.

At compile time, the compiler actually changes the product.IsCandy() call to this call:

MyExtensions.IsCandy(product)

Surprisingly, extension methods can be defined for sealed classes. As we all know a sealed class, by definition, is a class that cannot be modified in any way, for example, being inherited or overridden. However with extension methods, we can add as much functionality to a sealed class as we like. This gives us an opportunity to improve a class that we don't have ownership of. In our example, you can change the Product class to be sealed and it still runs without any problem. This gives us great flexibility to extend system types because many of the system types are sealed.

On the other hand, extension methods are less discoverable and are harder to maintain, so they should be used with great caution. If your requirements can be achieved with an instance method, you should not define an extension method to do the same work.

Not surprisingly, this feature is again a Visual Studio compiler feature and the compiled assembly is a valid .NET assembly.

Extension methods are the basis of LINQ. We will discuss the various LINQ extension methods defined by .NET Framework in the System.Linq namespace later (the actual LINQ extension methods are defined in the System.Core assembly, which is added to every project by default).

Now the Program.cs file should be as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestLINQFeaturesApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // valid var statements
            var x = "1";
            var n = 0;
            string s = "string";
            var s2 = s;
            s2 = null;
            string s3 = null;
            var s4 = s3;
            /*
            string x = "1";
            int n = 0;
            string s2 = s;
            string s4 = s3;
            */

            // invalid var statements
            /*
            var v;
            var nu = null;
            var v2 = "12"; v2 = 3;
            */

            // old way to create and initialize an object
            /*
            Product p = new product(1, "first candy", 100.0m);
            Product p = new Product();
            p.ProductID = 1;
            p.ProductName = "first candy";
            p.UnitPrice=100.0m;
            */
            //object initializer
            
            var product = new Product
            {
                ProductID = 1,
                ProductName = "first candy",
                UnitPrice = (decimal)100.0m
            };
            var arr = new[] { 1, 10, 20, 30 };

            // collection initializer
            var products = new List<Product> {
                new Product { 
                    ProductID = 1, 
                    ProductName = "first candy", 
                    UnitPrice = (decimal)10.0m },
                new Product { 
                    ProductID = 2, 
                    ProductName = "second candy", 
                    UnitPrice = (decimal)35.0m },
                new Product { 
                    ProductID = 3, 
                    ProductName = "first vegetable", 
                    UnitPrice = (decimal)6.0m },
                new Product { 
                    ProductID = 4, 
                    ProductName = "second vegetable", 
                    UnitPrice = (decimal)15.0m },
                new Product { 
                    ProductID = 5, 
                    ProductName = "third product", 
                    UnitPrice = (decimal)55.0m }
            };
            // anonymous types
            var a = new { Name = "name1", Address = "address1" };
            var b = new { Name = "name2", Address = "address2" };
            b = a;
            /*
            class __Anonymous1
            {
                private string name;
                private string address;
                public string Name {
                    get{
                        return name;
                    }
                    set {
                        name=value
                    }
                }
                public string Address {
                    get{
                        return address;
                    }
                    set{
                        address=value;
                    }
                }
            }
            */

            // extension methods
            //if(MyExtensions.IsCandy(product))
            if (product.IsCandy()) 
                Console.WriteLine("yes, it is a candy");
            else
                Console.WriteLine("no, it is not a candy");
        }
    }

    
    public sealed class Product
    {
        public int ProductID { get; set; }
        public string ProductName { get; set; }
        public decimal UnitPrice { get; set; }
    }

    public static class MyExtensions
    {
        public static bool IsCandy(this Product p)
        {
            if (p.ProductName.IndexOf("candy") >= 0)
                return true;
            else
                return false;
        }
    }
}

So far in Program.cs, we have carried out the following:

  • Defined several var type variables
  • Defined a sealed class called Product
  • Created a product with the name of "first candy"
  • Created a product list containing five products
  • Defined a static class and added a static method called IsCandy with a this parameter of the Product type to it, making this method an extension method
  • Called the extension method on the candy product and printed out a message according to its name

If you run the program, the output will look like the one shown in the following screenshot:

Extension methods
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset